Sara Soueidan – Blog
Articles on front-end design engineering by Sara Soueidan
2024-01-15T00:00:00Z
https://sarasoueidan.com/
Sara Soueidan
hello@sarasoueidan.com
Accessible notifications with ARIA Live Regions (Part 2)
2024-01-15T00:00:00Z
https://sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-2/
<p>In the <a href="https://sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-1">first part of this chapter</a> we discussed what we might need live regions for, and how to create them using HTML and ARIA.</p>
<p>In this part, we’re going to discuss what live regions are <em>not</em> suitable for and why, and we’re going to discuss more robust ways to implement some common UI patterns that you might otherwise consider using live regions for. After that, we’re going to go over some best practices for implementing live regions for when you do need to use them to represent things like status messages in your web applications.</p>
<p>Live regions are easy to misuse and to <em>overuse</em>. Aside from inconsistent browser and screen reader support, live regions’ inherent capabilities are limited <em>by design</em>, which makes them unsuitable for certain types of content updates.</p>
<h2 id="live-regions-don%E2%80%99t-handle-rich-text" tabindex="-1">Live regions don’t handle rich text</h2>
<p>Live regions don’t handle rich text. This means that the semantics of elements like headings, lists, links, buttons, and other structural or interactive elements are not conveyed when the contents of a live region are announced. If a live region contains a button, for example, the screen reader will announce the text of the button when it is injected into the live region without any mention of the button’s role:</p>
<figure>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- The semantics of this button are not conveyed in the live region announcement --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>You'll have to guess what I represent!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>
<p>The fact that the text represents the label of a button will not be communicated by the screen reader in the live region announcement. (Example borrowed from <a href="https://www.scottohara.me/blog/2022/02/05/dynamic-results.html">Scott O’hara’s article</a>)</p>
</figcaption>
</figure>
<p>Here is how VoiceOver with Safari announces a button when I add the button to a live region using JavaScript:</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/button-in-live-region.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
</figure>
<p>The screen reader will announce the entire contents of a live region as one long string of text, without any of the structure.</p>
<p>This is why <strong>you should not wrap entire sections of content in a live region.</strong> Otherwise the entire section’s content will be announced as one long string of text, which would result in a bad user experience.</p>
<p>When content updates happen in large sections of content, there is often a better way to communicate these updates to screen reader users.</p>
<p>For example, say you’re building a filtering component for an e-commerce website or any website that offers the ability to filter content within a main section of the site.</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/nike-filters-600w.webp 600w, https://sarasoueidan.com/assets/images/nike-filters-900w.webp 900w, https://sarasoueidan.com/assets/images/nike-filters-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/nike-filters-600w.jpeg 600w, https://sarasoueidan.com/assets/images/nike-filters-900w.jpeg 900w, https://sarasoueidan.com/assets/images/nike-filters-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="Screenshot of the Nike website showing a products page that provides product filters on the left side of the page." class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/nike-filters-600w.jpeg" width="1500" height="937" /></picture>
</figure>
<p>In most web applications, the content in the main section will filter dynamically as soon as the user selects one of the available filters. But <strong>just because the content within the section gets updated does not mean that the section should be a live region.</strong></p>
<p>So, how <em>do</em> you let the user know that the content in the section is updating?</p>
<p><strong>Providing simple instructional cues that set the user’s expectation of what will happen when they interact with an element is sometimes more than sufficient to let the user know of content updates even before they happen.</strong></p>
<p>For the filtering component, what this means is that you can include an instructional cue at the top of the group of filters to let the user know that changing the filters will change the content in the main area. This way you’re setting the user’s expectation of what will happen when they select a filter, so you no longer need to make any announcements when the content updates. The user knows that they can just navigate to the main area and start exploring the filtered content.</p>
<p>The <a href="https://www.w3.org/WAI/WCAG21/quickref/?showtechniques=413&currentsidebar=%23col_customize#status-messages">WCAG Quick Reference</a> website provides a live implementation of this approach.</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/wcag-filters-600w.webp 600w, https://sarasoueidan.com/assets/images/wcag-filters-900w.webp 900w, https://sarasoueidan.com/assets/images/wcag-filters-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/wcag-filters-600w.jpeg 600w, https://sarasoueidan.com/assets/images/wcag-filters-900w.jpeg 900w, https://sarasoueidan.com/assets/images/wcag-filters-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="Screenshot of the WCAG Quick reference website highlighting the filters section in the left sidebar, with a note at the top of the section that says that Changing filters will change the listed Success Criteria and Techniques." class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/wcag-filters-600w.jpeg" width="1500" height="937" /></picture>
<figcaption>
Preceding the content filters in the left sidebar of the Quick Reference, there is a note that lets the user know that “Changing filters will change the listed Success Criteria and Techniques”. No live region is necessary when this persistent cue is shown to all users, including screen reader users.
</figcaption>
</figure>
<p>Another very common UI component that can benefit from a similar implementation approach is a dynamic search component — particularly one where you have a search field that filters and displays results as you type into the field, like the search component you can find on <a href="https://www.smashingmagazine.com/">the Smashing Magazine website</a>.</p>
<aside role="note">
Implementing a dynamic search component like this one was one of the most popular requests that I got when I asked many of you what you'd like me to discuss in this course. So, here goes!
</aside>
<p>What many developers will do when they build a similar component (and, admittedly, it’s a mistake I also made early in my career) is they will designate the search results container as a live region (like Smashing Magazine currently does) just because the content in the container is dynamically updated while the user is typing in the field. This results in a very noisy user experience. Here’s how VoiceOver on macOS starts announcing the contents of the results container after I type a search keyword in it:</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/vo-smashing-search.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
</figure>
<p>A simpler, more robust, and much more user-friendly approach to implementing this pattern is to provide an instructional cue (i.e. an accessible description for the input field) that tells the user what will happen when they start typing in the field. The description might say “Results will [ filter / display / etc. ] as you type”. For example, the search component on <a href="https://a11ysupport.io/">the a11ysupport.io website</a> provides a live implementation of this approach. The accessible description of the search field lets you know that “Features will be filtered as you type”.</p>
<p>The cue must be associated with the search field using <code>aria-describedby</code> so that screen readers announce it to their users.</p>
<p>Providing instructional cues is helpful for all users. But if you don’t want the description to be visible, you can visually hide it using <a href="https://practical-accessibility.today/chapters/hiding-techniques/">the <code>visually-hidden</code> utility class</a>. Just make sure it is properly associated with the input field using <code>aria-describedby</code>.</p>
<p>By letting the screen reader user know that results will be shown as they type, you no longer need to announce when results are shown, and the user knows that they can navigate to the search results once they’re done typing their keyword in the field.</p>
<p>That being said, <strong>you do still want to use an assertive live region to inform the user when <em>no</em> results are found.</strong></p>
<p><q>While we don’t want to constantly interrupt people while typing, we need to interrupt when things go afoul</q>, says accessibility engineer Scott O’Hara in his article <a href="https://www.scottohara.me/blog/2022/02/05/dynamic-results.html">Considering dynamic search results and content</a>. <q>Specifically, let someone know immediately when they have entered a query that returns no results. Delaying such an announcement would result in wasted time, and potential uncertainty about “when” someone’s query stopped working. […] People who can see the UI are likely going to notice right away when the dynamic content dries up. They can then immediately correct for this by adjusting their query. This same affordance must be provided to people with disabilities.</q></p>
<p>In his article, Scott elaborates more on all the usability considerations you should keep in mind when you’re implementing a dynamic search component. He then proposes a solution for how you might go about implementing it in a more robust and inclusive manner. I highly recommend checking the article out and following his implementation pattern if you can.</p>
<p>In addition to using an accessible description for the search field, and a live region to announce when no results are found, Scott also suggests moving the user’s keyboard focus to the heading that introduces the results when the <kbd>Enter</kbd> key is pressed. Of course, if your search component doesn’t provide a heading, you wouldn’t need to do that. But it is a nice addition that improves the user experience for keyboard users, and screen readers will announce the number of results found when focus is moved to the heading.</p>
<p>Here is how Scott’s demo works with NVDA on Firefox:</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/nvda-scott-search.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>
In this video, NVDA announces the search field followed by the accessible description when my focus moves to the field. The accessible description indicates that results will be shown as I type. I first type a keyword "one" into the search field. Then I press Enter. Pressing Enter moves keyboard focus to the heading which introduces the results and communicates the number of results found. Then I press the Escape key. Pressing the Escape key moves my focus back to the search field. When I type a keyword that's more than five characters long, the dummy example says that No results are found. Since Scott is using a live region to communicate when no results are found, NVDA announces "No results found!".
</figcaption>
</figure>
<p>Here’s an embed of Scott’s demo:</p>
<figure class="wide">
<p class="codepen" data-height="492.4932861328125" data-default-tab="html,result" data-slug-hash="zYPByjo" data-user="scottohara" style="height: 492.4932861328125px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/scottohara/pen/zYPByjo">
quick demo of showing / informing about dynamic results</a> by Scott (<a href="https://codepen.io/scottohara">@scottohara</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
</figure>
<h2 id="live-regions-are-not-suitable-for-notifications-with-interactive-elements" tabindex="-1">Live regions are not suitable for notifications with interactive elements</h2>
<p>Live regions should not be used for messages or notifications that contain interactive elements, particularly if the user may need to act on those notifications.</p>
<p>As we mentioned earlier, when a screen reader announces the contents of a live region, <strong>it will announce the raw text content within the region without any of the structure or semantics. This means that the semantics of any interactive elements will not be conveyed.</strong></p>
<p>Furthermore, when an update happens in a live region, screen readers will only announce the contents of a live region, but <strong>the user’s focus does not move to the region.</strong> And there is no mechanism available to allow the user to easily navigate to a live region to interact with any content that might be in it.</p>
<p>So unless you provide a clear path for screen reader and keyboard users to get to the notification that contains interactive elements (like a well-documented keyboard shortcut, for example), then, depending on the position of the live region in the DOM, it can be difficult—if not impossible—for the user to get to the interactive content in the notification, especially if the notification dismisses itself after a short timeout.</p>
<p>This is why toast messages that contain interactive elements are problematic. Unfortunately, toast messages that contain interactive elements are pretty common. You can see examples of them documented in <a href="https://m1.material.io/components/snackbars-toasts.html#snackbars-toasts-usage">Google’s Material Design system</a>.</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/toast-with-interactive-element-600w.webp 600w, https://sarasoueidan.com/assets/images/toast-with-interactive-element-900w.webp 900w, https://sarasoueidan.com/assets/images/toast-with-interactive-element-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/toast-with-interactive-element-600w.jpeg 600w, https://sarasoueidan.com/assets/images/toast-with-interactive-element-900w.jpeg 900w, https://sarasoueidan.com/assets/images/toast-with-interactive-element-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="An example of a toast message with an interactive element from the Material Design System. The message says " Connection="" timed="" out.="" Showing="" limited="" messages.="" Retry.""="" class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/toast-with-interactive-element-600w.jpeg" width="1500" height="434" /></picture>
</figure>
<p>But these messages come with usability and accessibility problems for screen reader users, as well as other users of assistive technologies like users browsing the web using a magnifier, and keyboard users as well.</p>
<p>Adrian Roselli has documented and listed <a href="https://adrianroselli.com/2020/01/defining-toast-messages.html#WCAG">the most relevant WCAG failures that toast messages will typically be in violation of</a>, particularly if they contain interactive elements. I highly recommend pausing here and taking a couple of minutes to read Adrian’s article, especially if you’re considering using toasts in your applications.</p>
<p>If a notification contains an interactive element, you need to ensure that the user can easily navigate to it. And the best way to do that is to move the user’s focus to it.</p>
<p>The <code>alert</code> and <code>status</code> live region roles are meant to represent short messages that do not require moving the user’s focus to (i.e. that do not contain interactive children).</p>
<blockquote>
<p>Authors SHOULD ensure an element with role <code>status</code> does not receive focus as a result of change in status.</p>
<p>[…]</p>
<p>Neither authors nor user agents are required to set or manage focus to an alert in order for it to be processed. Since alerts are not required to receive focus, authors SHOULD NOT require users to close an alert. If an author desires focus to move to a message when it is conveyed, the author SHOULD use <code>alertdialog</code> instead of alert.</p>
<p>— <a href="https://www.w3.org/TR/wai-aria-1.2/#alert">The ARIA Specification</a></p>
</blockquote>
<p>If a notification contains an interactive element, it should not be a live region. And it should also not be a toast. You should move the user’s focus to it instead, and make it persistent.</p>
<p>For interactive alert notifications, instead of using a toast message, consider using an alert dialog. The ARIA <a href="https://w3c.github.io/aria/#alertdialog"><code>alertdialog</code> role</a> is used to represent a type of dialog that contains an alert message.</p>
<p>As the name implies, <code>alertdialog</code> is a mashup of the <code>dialog</code> and <code>alert</code> roles. This means that it also expects similar keyboard interactions as modal dialogs do. Implementing an alert dialog is outside the scope of this chapter, but you can find the semantic and keyboard interaction requirements for implementing accessible alert dialogs <a href="https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/">documented on the APG website</a>.</p>
<aside role="note">
<p>Using <code>alertdialog</code> to alert the user of the presence of errors is <a href="https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA18.html">an advisory technique</a> to meet SC 4.1.3 Status Messages.</p>
</aside>
<p>For status type notifications that contain interactive elements, you may use a modal or non-modal <a href="https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/">dialog</a> instead, and manage focus within these dialogs when they appear, as documented on the APG modal dialog example page.</p>
<p>Keep in mind that <strong>moving focus should be done as an immediate response to the user’s action</strong>. If something happens async or after a delay like if a toast appears that the user hasn’t called up, then you shouldn’t move their focus to it, otherwise it would be disruptive to the user experience. <strong>Decide if you should move focus or not based on what users are expecting.</strong></p>
<h2 id="live-regions-are-not-a-substitute-for-aria-state-properties" tabindex="-1">Live regions are not a substitute for ARIA state properties</h2>
<p>Don’t use live regions to convey state changes when there’s an ARIA attribute to do that.</p>
<p>For example, if a button toggles the visibility of some content on a page (like a ‘dropdown’), use the <code>aria-expanded</code> attribute to communicate the state of the content (whether it’s expanded or collapsed) to the user. You don’t need a live region to announce that the content has been expanded or collapsed.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- The aria-expanded attribute communicates the state of the disclosure widget to the user. No live region is needed to do that. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ true | false ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Terms of use<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Similarly, if you’re building a dark theme switcher using a toggle button, for example, use <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-pressed">the <code>aria-pressed</code> attribute</a> to communicate to screen readers that the dark theme is currently ‘On’ or ‘Off’.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- The aria-pressed attribute communicates whether or not the [Dark Theme] is On or Off. No live region is needed to announce when the dark theme is applied. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-pressed</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ true | false ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Dark theme<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>When <code>aria-pressed</code> is declared on a <code><button></code>, the button’s ARIA role mapping will change in most accessibility APIs, and it will be exposed as a <code>toggle button</code> (not just a regular button), indicating that this <code><button></code> toggles a certain functionality On and Off. When the value of <code>aria-pressed</code> is true, it communicates to screen readers that the functionality is currently ‘On’. Combined with the button’s accessible name, the state attribute lets the user know what will happen when they activate the button. You don’t need a live region to announce that the dark theme has been applied to the page.</p>
<p>Here’s a live demo of a simple theme switcher using two toggle buttons that you can try using a screen reader:</p>
<figure class="wide">
<p class="codepen" data-height="361.2337646484375" data-default-tab="html,result" data-slug-hash="NWeEqKJ" data-user="SaraSoueidan" data-token="7222cd9490a0c6a36a25b0ba42ed7e75" style="height: 361.2337646484375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/NWeEqKJ/7222cd9490a0c6a36a25b0ba42ed7e75">
Untitled</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<figcaption>
<p>When a toggle button is activated, the screen reader announces the state of the button (whether it’s pressed or not) when it announces the button’s role and accessible name. The state of the button is sufficient to communicate when a theme is ‘On’ or ‘Off’. You can try it for yourself in <a href="https://codepen.io/pen/debug/NWeEqKJ/7222cd9490a0c6a36a25b0ba42ed7e75">the debug version of this theme switcher</a>.</p>
</figcaption>
</figure>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>State property changes may not be announced to the user the same way live regions are, but not every change <em>needs</em> to be announced. Using appropriate <a href="https://practical-accessibility.today/chapters/html-semantics/">semantics</a>, providing meaningful <a href="https://practical-accessibility.today/chapters/accName/">accessible names</a>, and using the appropriate state attributes is sometimes sufficient for screen reader users to understand what will happen when they interact with an element.</p>
<p>So before considering using a live region, ask yourself if there is a state attribute that does what you need. And if there is, use that attribute.</p>
<h2 id="best-practices-for-implementing-(more-robust)-status-messages-with-live-regions" tabindex="-1">Best practices for implementing (more robust) status messages with live regions</h2>
<p>Live regions are most suited for implementing short, non-interactive status messages that do not cause a change of context (like moving focus) and that cannot be communicated to screen reader users in another way. Live regions are currently the primary way to conform with <a href="https://www.w3.org/WAI/WCAG21/quickref/?showtechniques=413#status-messages">Success Criterion <strong>4.1.3 Status Messages (Level AA)</strong></a>.</p>
<p>If you’re implementing status messages in your web application(s), there are some best practices that most accessibility professionals agree on that can help you achieve maximum compatibility across browser and screen reader pairings:</p>
<h3 id="make-sure-the-live-region-container-is-in-the-dom-as-early-as-possible" tabindex="-1">Make sure the live region container is in the DOM as early as possible</h3>
<p>The element that is designated as a live region <strong>must exist on the page when the browser parses the contents and creates the accessibility tree of the page.</strong> This ensures that the element will be monitored for changes when they happen and that these changes are communicated to the screen reader and the user.</p>
<p>So when you create a live region, insert it into the DOM as soon as possible (ideally, when the page loads), <strong>before you push any updates to it.</strong> If you insert a live region into the DOM or convert a container into a live region <em>when you need it</em>, there’s a high chance that it won’t work.</p>
<h3 id="choose-an-appropriate-hiding-technique-if-the-live-region-isn%E2%80%99t-visible" tabindex="-1">Choose an appropriate hiding technique if the live region isn’t visible</h3>
<p>If the status message is not visible to all users, hide it visually using <a href="https://practical-accessibility.today/chapters/hiding-techniques/#using-visually-hidden-text">the <code>visually-hidden</code> utility class</a>.</p>
<p>Don’t hide the live region using using <code>display: none;</code> or <code>aria-hidden="true"</code>, or any other <a href="https://practical-accessibility.today/chapters/hiding-techniques/">hiding technique</a> that removes it from the accessibility tree. <strong>Hidden live regions are not announced.</strong></p>
<h3 id="limit-the-number-of-live-regions-on-the-page" tabindex="-1">Limit the number of live regions on the page</h3>
<p>While there is no rule as to how many live regions you can have on a page, you should limit the number of live regions you create.</p>
<p>As accessibility engineer Scott O’Hara says in his article <a href="https://www.scottohara.me/blog/2022/02/05/are-we-live.html">“Are we live?”</a>:</p>
<blockquote>
<p>please do keep in mind that something just as bad as live regions being injected into a web page and then making no announcements, is a web page with a bunch of live regions that all start barking at assistive technology users at the same time.</p>
</blockquote>
<p>A good practice is to have only two live regions on the page: <strong>one assertive region and one polite region</strong> that get inserted to the page on page load. Then you insert updates into these two regions and manage the message queue in them via JavaScript.</p>
<p>If you have multiple live regions on a page, they may interfere with each other, and some messages might not be announced at all.</p>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#aria-live">According to the specification</a>:</p>
<blockquote>
<p>Items which are assertive will be presented immediately, followed by polite items. User agents or assistive technologies MAY choose to clear queued changes when an assertive change occurs. (e.g., changes in an assertive region may remove all currently queued changes)</p>
</blockquote>
<p>What this means is that <strong>the politeness level</strong> indicated by the <code>aria-live</code> attribute <strong>works as an ordering mechanism for updates</strong>. In instances when you have multiple live regions on a page, <code>polite</code> updates take a lower priority; and <code>assertive</code> updates take a higher priority and could even potentially clear or cancel other updates that are queued for announcement. This causes some messages to get lost, or just partially announced.</p>
<!-- Limiting the number of live regions to one of each type also allows you to manage the message queue more efficiently and avoids race conditions. -->
<p>Carefully choreographing the sequence of events in a couple of live regions on the page will be your best approach to achieve maximum compatibility.</p>
<aside role="note">
<p>This is one of the reasons why, in <a href="https://practical-accessibility.today/chapters/form-validation-2/">the previous chapter</a>, we said that providing a summary of errors at the top of a form is a far more robust approach than using inline validation.</p>
</aside>
<!-- And last but not least, **too many live regions can slow the page performance when you're running a screen reader.** -->
<h3 id="compose-and-insert-your-message-into-the-live-region-in-one-go" tabindex="-1">Compose and insert your message into the live region in one go</h3>
<p>You should <strong>pre-compose the notification message’s content and insert it into the region in one go.</strong> Don’t make multiple DOM insertions to create one message, otherwise the screen reader may make multiple <em>separate</em> message announcements, which is not what you want.</p>
<h3 id="keep-the-content-short-and-succinct-and-avoid-rich-content" tabindex="-1">Keep the content short and succinct and avoid rich content</h3>
<p><strong>Keep the message content concise.</strong> And keep it as short as possible. There is no character limit on the notification message, but remember that live region announcements are transient and can’t be re-played. So make sure the message is easy to understand when it is announced the first time. Keeping it short also ensures it is less disruptive to the user flow.</p>
<p><strong>Avoid rich content, interactive elements, and non-text elements</strong> like images as these are also not conveyed to screen reader users.</p>
<h3 id="empty-the-live-region-and-wait-a-bit-in-between-updates" tabindex="-1">Empty the live region and wait a bit in between updates</h3>
<p>If the status message is not visible to everyone or it is removed after a short timeout, set a timeout (e.g. 350ms–500ms) to remove the notification text from the live region. You are not required to empty a live region after its contents have been announced because announcements are triggered on content additions by default, but emptying the live region between updates ensures that you don’t end up with weird or duplicate announcements.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">/* set a timeout to empty the live region */</span></span><br /><span class="highlight-line"><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">//empty live region</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">350</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Emptying the live region when it’s no longer visible also ensures that screen reader users will not be able to navigate to it when they are not intended to.</p>
<p>So make sure the live regions are cleared up in between updates, and wait a little bit before inserting new updates to them. And when you do insert a new update, insert the new message in one go.</p>
<h2 id="debugging-live-regions" tabindex="-1">Debugging Live Regions</h2>
<p>If you use live regions, you’re going to want to debug them when they don’t work as expected, like when the screen reader announces something unexpectedly. Part of debugging live regions is seeing what goes inside in them and when.</p>
<p>To debug live regions on a page, you can use the <a href="https://chrome.google.com/webstore/detail/nerderegion/lkcampbojgmgobcfinlkgkodlnlpjieb/related?hl=en-US">NerdeRegion</a> browser extension.</p>
<p>NerdeRegion is a developer tools extension for debugging live regions on a Web Page. When activated, it lists all active ARIA live regions, and keeps a record of all mutations that has happened on the region.</p>
<p>You can use NerdeRegion to:</p>
<ul>
<li>Check if a live region is being updated properly,</li>
<li>Check if accessible name computation (beta) is done correctly,</li>
<li>Check if live region is being re-used correctly.</li>
</ul>
<p>To use the extension, open your browser’s Developer Tools, and navigate to the NerdeRegion tab. There, you can keep track of timestamped announcements and the source element they originate from.</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/nerderegion.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>
In this video, I have an assertive live region on the page. When I go to the NerdeRegion panel in the Edge DevTools, it lists the number of live regions on the page in the left sidebar, as well as the type of the live region (assertive or polite) in the main area of the panel. Then when I activate the button that populates the live region with a new message, NerdeRegion shows when the live region has changed, along with a time stamp of when it did.
</figcaption>
</figure>
<p>Since there can be bugs and inconsistencies with how ARIA live regions are announced with different screen readers, you should constantly be reviewing your live regions to ensure they are continuing to work as necessary.</p>
<p>As we mentioned earlier, you should try to limit the number of live regions you use ideally to two or less. But if you absolutely have to use more than that, NerdeRegion can help you figure out if an issue is potentially caused by your code or by the device combination.</p>
<h2 id="avoid-live-regions-if-you-can" tabindex="-1">Avoid live regions if you can</h2>
<p>I know this isn’t the advice you’d expect at the end of a whole chapter about live regions. But hear me out.</p>
<p>Live regions are inconsistent and unpreditcable. It’s easy for their implementations to go wrong. There’s a lot of manual work involved to get them working properly.</p>
<p>Furthermore, the design of live regions is intended to give maximum flexibility to screen readers to implement an experience that is best for their users.</p>
<p>What this means is that ARIA live region properties are only <strong>strong suggestions</strong> as to how you want live region announcements to be made, but the value of these properties (and by extension: the behavior of live regions) <strong>may be overridden by browsers, assistive technologies, or by the user.</strong> This along with current bugs and implementation gaps means that you can’t guarantee that a live region will always work the way you designed it to. This is one of the reasons why you should try to rely on live regions as little as possible, and use alternative and more robust approaches whenever you can.</p>
<p>Remember that live region announcements are transient. If the user misses an announcement, they miss it. Depending on the importance and urgency of the announcement, this can easily degrade the usability of your web application and result in a frustrating user experience.</p>
<p>So if you can make your users aware of updates using other more persistent methods like moving focus or providing instructional cues, then you should consider doing so.</p>
<p><strong>Not everything that updates in the background needs to be a live region.</strong> For example, chat interfaces are typically a great candidate for live regions and would be implemented using the <code>log</code> ARIA role, but they don’t always <em>need</em> to be implemented as live regions. Unless the chat is the main interface on the page, then it probably <em>shouldn’t</em> even be a live region.</p>
<p>For your day-to-day work, you’ll need live regions less often than you think, even if you’re building dynamic web applications like SPAs.</p>
<p>For example, let’s say you’re building the navigation for a SPA. In most SPA navigations, activating a link will load a new page without causing a page refresh.</p>
<p>Normally when the user activates a link and the link takes them to a new page, screen readers will announce the title of the new page first, which lets the user know where they have landed. But this doesn’t happen in SPAs. So what many developers will do is they will use live regions to announce when new content has been loaded. But this is not only unnecessary, but you can even let the user know that new content has loaded in a more efficient way.</p>
<p>Instead of relying on a live region to announce the page change, you could send keyboard focus to the main <code><h1></code> of the page which, as we mentioned in the <a href="https://practical-accessibility.today/chapters/heading-structure/">heading structure chapter</a>, should describe the primary topic of the contents of the page and ideally be identical to the page’s <code><title></code>.</p>
<p>By moving the user’s focus to the heading, the screen reader announces the heading’s content to the user, which gives them the same context that the page’s <code><title></code> would have given them if it had been announced after a page refresh. (But don’t forget to change the page’s <code><title></code> when a new page is loaded, too.)</p>
<p>Moving focus to the primary heading of the page is also helpful for keyboard users. Usually when the page refreshes and the user starts tabbing through the page, a <a href="https://practical-accessibility.today/chapters/skip-links/">skip link</a> should be the first element they focus on, and they can use that link to skip directly to the main content of the page. But if the page doesn’t refresh, they may have to tab their way through many elements before they reach the new content. So moving their focus to the main heading makes their navigating through the page more efficient.</p>
<p>Live regions have their (limited) use cases — particularly for status messages as described in WCAG. But as accessibility engineer Scott O’Hara says:</p>
<blockquote>
<p>if you can create an interface that can limit the number of live regions necessary - none being the ideal - then that’d be for the better.</p>
</blockquote>
<p>Your main purpose as a designer or developer is to make users aware of new content updates when they happen. But for most UI patterns, there are other ways you can achieve that, and those ways are often more robust and more reliable than live regions, and result in an overall better experience for your users.</p>
<p>Another example that <em>could</em> use live regions but that can also be implemented in a more robust manner is a shopping cart on an e-commerce website. A common pattern on many websites today is to show an overlay of the full cart when a new item is added to the cart.</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/overlay-cart-600w.webp 600w, https://sarasoueidan.com/assets/images/overlay-cart-900w.webp 900w, https://sarasoueidan.com/assets/images/overlay-cart-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/overlay-cart-600w.jpeg 600w, https://sarasoueidan.com/assets/images/overlay-cart-900w.jpeg 900w, https://sarasoueidan.com/assets/images/overlay-cart-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="Screenshot of the A Book Apart website showing a modal cart overlay open. This overlay appears when an item is added to cart." class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/overlay-cart-600w.jpeg" width="1500" height="937" /></picture>
<figcaption>The modal cart overlay pattern used on the <a href="https://abookapart.com/">A Book Apart website</a>.</figcaption>
</figure>
<p>Instead of using live regions to announce that a new item has been added to cart, you can use this pattern and move the user’s keyboard focus to the cart when it is shown. This approach has a couple of benefits, one of them is that it makes it easier for keyboard users to get to the cart, see and/or edit what’s in it, and continue to checkout if this is what they want to do.</p>
<p>Keep in mind that you must treat the cart as a modal dialog in this case and manage focus accordingly, particularly when the contents of the page are dimmed after the cart is shown. You can find the <a href="https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/#keyboardinteraction">requirements for keyboard focus management for modal dialogs</a> in <a href="https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/">the Modal Dialog page on the APG website</a>.</p>
<p>As a general rule of thumb: if you can achieve the same result without using live regions, then probably do so. The less ARIA you use, the better. Remember: <a href="https://practical-accessibility.today/chapters/aria-rules/">No ARIA is better than bad ARIA</a>.</p>
<p>If you do use live regions in your web applications, make sure you <strong>thoroughly test</strong> across all browsers and screen reader pairings. And don’t forget to test with Braille displays, too. And perform usability tests with screen reader users. Not only will usability testing give you insights into what your users are expecting from the application and what they aren’t, but it will also help you understand the different ways screen reader users are using your application and how that will affect the announcements you’re trying to make with live regions.</p>
<h2 id="references%2C-resources-and-recommended-reading" tabindex="-1">References, resources and recommended reading</h2>
<ul>
<li><a href="https://www.youtube.com/watch?app=desktop&v=W5YAaLLBKhQ&t=7s">The Many Lives of a Notification</a></li>
<li><a href="https://webaim.org/techniques/screenreader/">Designing for Screen Reader Compatibility</a></li>
<li><a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">output: HTML’s native live region element</a></li>
<li><a href="https://www.scottohara.me/blog/2022/02/05/are-we-live.html">Are we live?</a></li>
<li><a href="https://talks.yatil.net/Ux0REi/were-aria-live">We’re ARIA Live</a></li>
<li><a href="https://dequeuniversity.com/library/aria/liveregion-playground">Live Region Playground</a></li>
<li><a href="https://pauljadam.com/demos/aria-atomic-relevant.html">(Test case demo) aria-atomic and aria-relevant on aria-live regions</a></li>
<li><a href="https://wicg.github.io/aom/notification-api.html">Accessibility (ARIA) Notification API</a></li>
<li><a href="https://adrianroselli.com/2020/11/more-accessible-skeletons.html">More accessible skeletons</a></li>
<li><a href="https://adrianroselli.com/2020/01/defining-toast-messages.html">Defining ‘Toast’ Messages</a></li>
<li><a href="https://www.scottohara.me/blog/2019/07/08/a-toast-to-a11y-toasts.html">A toast to an accessible toast…</a></li>
</ul>
<br />
<aside role="note">Many thanks to <strong>James Edwards</strong> (<a href="https://twitter.com/siblingpastry">@siblingpastry</a>) for reviewing this chapter.</aside>
Accessible notifications with ARIA Live Regions (Part 1)
2024-01-15T00:00:00Z
https://sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-1/
<p>In this chapter, we’re going to learn about ARIA live regions — the accessible notifications system that enables us to make <strong>dynamic</strong> web content more accessible to screen reader users.</p>
<p>Without live regions, some rich web applications would be more challenging to use for screen reader users. So if you’re building web applications such as Single Page Applications (SPAs), you need to understand live regions so that you can utilize them <strong>where appropriate</strong>.</p>
<p>This chapter is split into two parts. In <strong>this first part</strong>, we’re going to learn about why ARIA live regions are important, and the different ARIA attributes and roles that you can use to create them. We’re going to get an overview of these attributes, as well as learn about their current support landscape and limitations.</p>
<p>In <a href="https://sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-2">the second part</a>, we’re going to get more practical and discuss why you should <em>not</em> use live regions as much as you might think that you do, and we’ll talk about alternative approaches you should use instead when you create some common UI patterns. And then we’ll discuss best practices for implementing more robust live regions when you need them today.</p>
<p>But first, in order to understand <em>why</em> live regions are important, <strong>we must first understand how a screen reader parses web content and presents it to the user.</strong> We won’t get into much detail (not at all, really!), just enough to get a good understanding of what problem live regions solve.</p>
<h2 id="how-screen-readers-parse-web-content" tabindex="-1">How screen readers parse web content</h2>
<p>The way screen readers parse and present web content to their users is very different to how sighted users see that content.</p>
<p>Screen readers work by <strong>linearizing web content.</strong> Linearizing a page’s content means converting the page’s two-dimensional content into <strong>a one-dimensional string</strong> (that is then either spoken to the user using text-to-speech, or delivered to them via a refreshable braille display).</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/two-to-one-dimension-600w.webp 600w, https://sarasoueidan.com/assets/images/two-to-one-dimension-900w.webp 900w, https://sarasoueidan.com/assets/images/two-to-one-dimension-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/two-to-one-dimension-600w.jpeg 600w, https://sarasoueidan.com/assets/images/two-to-one-dimension-900w.jpeg 900w, https://sarasoueidan.com/assets/images/two-to-one-dimension-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="" class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/two-to-one-dimension-600w.jpeg" width="1500" height="558" /></picture>
</figure>
<p>When content is linearized, it is presented to the user <strong>one item at a time.</strong> <q>You can think of it like listening to a cassette tape</q>, says Web accessibility specialist Ugi Kutluoglu, <q>which you can rewind, fast forward, pause and play.</q> This means that a screen reader user can skip to items or sections they want, and they can tab through interactive elements and Shift-tab their way back. But at the end of the day, they can only move forwards or backwards, <strong>one item at a time</strong>, because what they are presented with is a one-dimensional version of the page.</p>
<h2 id="why-we-need-an-accessible-notification-system-for-screen-reader-users" tabindex="-1">Why we need an accessible notification system for screen reader users</h2>
<p>Reading content linearly works well for static webpages, but it doesn’t work so well for pages where content is altered and updated dynamically or asynchronously using JavaScript. If the user can only move in one dimension, and focus on one item at a time, how would they know when content is added, removed, or modified <strong>somewhere else</strong> on the page?</p>
<p>For example, when a user sends an email in most email web apps, a status message is shown at the top of the screen, or a “toast” message pops up (typically at the bottom of the screen) to notify them of the status of their interaction — for example that the email is sending, has been sent, or maybe that the email could <em>not</em> be sent. Some of these messages are urgent (like an error message), and some of them are not (like a success message, or a Draft Saved notification).</p>
<p>When these status messages appear, they are intended to be communicated to all users. But while these messages may be perceivable by a sighted user, they’re not preceivable by a blind screen reader user. When the status message is shown, it is not communicated to screen reader users by default because the screen reader focus is on another element at that moment (on the ‘Send Email’ button in this case).</p>
<figure class="wide">
<!-- <img src="../../assets/images/chapter--live-regions/gmail-notification.jpg" alt=""> -->
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/gmail-notification-600w.webp 600w, https://sarasoueidan.com/assets/images/gmail-notification-900w.webp 900w, https://sarasoueidan.com/assets/images/gmail-notification-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/gmail-notification-600w.jpeg 600w, https://sarasoueidan.com/assets/images/gmail-notification-900w.jpeg 900w, https://sarasoueidan.com/assets/images/gmail-notification-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="" class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/gmail-notification-600w.jpeg" width="1500" height="615" /></picture>
</figure>
<p>Here is what happens when I use NVDA and activate the Send Email button and show a status message in a dummy email app demo I created. NVDA does not announce the status message when it is shown.</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/nvda-no-announcement.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
</figure>
<p>Here is a dummy email app demo you can try for yourself.</p>
<figure class="wide">
<p class="codepen" data-height="512.5320434570312" data-default-tab="result" data-slug-hash="YzdByMb" data-user="SaraSoueidan" data-token="ee77a329a6469d35ca73d58136cf4ebc" style="height: 512.5320434570312px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/YzdByMb/ee77a329a6469d35ca73d58136cf4ebc">
Live region status message in email web app</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<figcaption>
<p>If you activate the Send Email button in <a href="https://codepen.io/pen/debug/YzdByMb/ee77a329a6469d35ca73d58136cf4ebc">the debug version of the dummy email app</a>, the screen reader will <em>not</em> announce the status message that is shown.</p>
</figcaption>
</figure>
<p><strong>A screen reader can only focus on one element or part of the page at a time.</strong> This means that if the user presses a button and that button triggers an update somewhere else on the page, <strong>there’s a good chance they will be oblivious to it</strong>. So they need a way to be notified of these updates when they happen.</p>
<!-- There may also be auto-updating elements on the page such as countdowns, or a loading indicator which updates when the element is loading or done loading. These elements update _while_ the user is doing something else on the page. So, they won't know what the status of these elements is unless they are somehow made aware of them. Form success and error messages like the ones we saw in [the previous chapter](/chapters/form-validation-2/) are also an example of updates that are also not automatically conveyed to screen reader users. -->
<p>There are two primary ways you make a screen reader announce an update when it happens:</p>
<ol>
<li>By <strong><em>moving</em> focus</strong> to where the update has happened, (like we did with the summary of error messages in <a href="https://practical-accessibility.today/chapters/form-validation-2/">the accessible form validation chapter</a>);</li>
<li>By <strong>notifying</strong> the screen reader of these updates when they happen.</li>
</ol>
<p>When you move the user’s focus to an element, screen readers typically announce the element to the user. But when an update happens and you don’t move the user’s focus to it, you must notify screen readers in some other way.</p>
<h2 id="status-messages-in-wcag" tabindex="-1">Status messages in WCAG</h2>
<p>WCAG <a href="https://www.w3.org/WAI/WCAG21/quickref/?showtechniques=413#status-messages">Success Criterion <strong>4.1.3 Status Messages (Level AA)</strong></a> states that:</p>
<blockquote>
<p>In content implemented using markup languages, status messages can be programmatically determined through role or properties such that they can be presented to the user by assistive technologies without receiving focus.</p>
</blockquote>
<p><a href="https://www.w3.org/TR/WCAG21/#dfn-status-messages">A status message is defined in the specification</a> as <q>change in content that is not a change of context, and that provides information to the user on the success or results of an action, on the waiting state of an application, on the progress of a process, or on the existence of errors.</q></p>
<aside role="note">
<p>Examples of a <strong>change of context</strong> are opening a new window, <strong>moving focus to a different component</strong>, going to a new page (including anything that would look to a user as if they had moved to a new page) or significantly re-arranging the content of a page.</p>
</aside>
<p>From the <a href="https://www.w3.org/WAI/WCAG21/Understanding/status-messages.html">Understanding Status Messages page</a>:</p>
<blockquote>
<p>This Success Criterion specifically addresses scenarios where new content is added to the page without changing the user’s context. Changes of context, by their nature, interrupt the user by taking focus. They are already surfaced by assistive technologies, and so have already met the goal to alert the user to new content. As such, messages that involve changes of context do not need to be considered and are not within the scope of this Success Criterion.</p>
</blockquote>
<p>In other words, this success criterion aims to ensure that, unless you move the user’s focus or cause another change of context like a page refresh, you must ensure that status messages are communicated to screen reader users using the appropriate roles and properties. To do that, you currently need ARIA live regions.</p>
<h2 id="what-are-aria-live-regions%3F" tabindex="-1">What are ARIA live regions?</h2>
<p>ARIA live regions are <strong>a specific type of notification system primarily surfaced for screen reader users.</strong> Using live regions, you can communicate content updates down to the accessibility layer so that screen readers are made aware of these updates when they happen.</p>
<p>On an implementation level, a <strong>live region</strong> (<strong>not to be confused with the <code>region</code> landmark</strong>) is an element on the page that has been designated as being “live”. When an element is designated as a live region, <strong>a screen reader is notified when any updates take place within the element (and its children), wherever its focus is at the time.</strong></p>
<p><q>Think of live regions as something like a livestream</q> says Web accessibility specialist Ugi Kutluoglu, <q>everything happening inside will be announced live like a news channel you’re listening to in the background.</q></p>
<p>Using live regions, you can mark up status messages and other similar updates so that they are communicated to screen reader users.</p>
<p>Here is our email notification example again with ARIA live regions working. Notice how when the ‘Send Email’ button is activated, NVDA announces the contents of the status message that is shown:</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/nvda-live-regions-demo.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
</figure>
<p>The screen reader announces the contents of the message because I’ve designated the message container as a live region (we’re going to learn how to do that shortly). So now the element is monitored for updates and the screen reader is notified of these updates when they happen. Then, when the button is activated, I inserted the contents of the message into the message container. And when I did, the screen reader was notified and it announced the update to the user.</p>
<p>When an update happens in a live region, the screen reader announces that update, and it only announces it once.</p>
<p><a href="https://m1.material.io/components/snackbars-toasts.html#snackbars-toasts-usage">Toast messages</a> and other similar status messages are the closest <strong>visual equivalent</strong> of a live region announcement. (Though not all toast messages are a good candidate for live regions. We’ll talk more about this later.)</p>
<p>A toast message is used to present <strong>timely information</strong> — including confirmation of actions, statuses, and alerts. By nature, <strong>toast messages are auto-expiring</strong> — they disappear on their own after a few seconds. And once they disappear, they’re gone. The user cannot review the message again.</p>
<p>Like toasts, <strong>live region notifications are transient.</strong> <strong>Once an announcement is made, it disappears forever.</strong> They cannot be reviewed, replayed, or revealed later. If the user misses an announcement, they miss it. It’s gone. That is, unless you provide them with a way to review it (like collecting all notifications in a notifications center, for example).</p>
<p>Because of their transient nature, live regions have specific and limited use cases and should not be used as an alternative to other more persistent approaches. In fact, if you <em>can</em> use another more persistent approach, you almost definitely should. We’ll talk more about how to use live regions and when <em>not</em> to use live regions later in the chapter.</p>
<h2 id="creating-a-live-region" tabindex="-1">Creating a live region</h2>
<p>Using ARIA, <strong>almost any element can be designated as a live region</strong>. It doesn’t need to be a structural element; and it doesn’t need to have any implicit semantics by default.</p>
<p>You can designate an element as a live region using:</p>
<ol>
<li>The <code>aria-live</code> attribute.</li>
<li>ARIA live region roles.</li>
</ol>
<p>HTML also provides one native element that has implicit live region semantics: the <code><output></code> element. We’re going to talk more about it in another section.</p>
<h3 id="1.-using-the-aria-live-attribute" tabindex="-1">1. Using the <code>aria-live</code> attribute</h3>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#aria-live"><code>aria-live</code></a> is the primary attribute used <strong>to designate an element as a live region.</strong> When used on an element, it indicates that this element may be updated, and those updates should be communicated to screen readers.</p>
<p>The value of <code>aria-live</code> <strong>describes the types of updates</strong> that can be expected from the region. It accepts three values: <code>assertive</code>, <code>polite</code>, and <code>off</code> (which is equivalent to removing the property altogether).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- this div is now a live region! It's as simple as that. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ polite | assertive ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The value of <code>aria-live</code> you choose will depend on <strong>the type, urgency and priority of the update.</strong></p>
<ul>
<li>If the update is important enough that it requires the user’s <strong>immediate attention</strong>, <code>assertive</code> will tell the screen reader to <em>immediately</em> notify the user, <strong>interrupting whatever the user’s currently doing.</strong> Assertive notifications are good for when users need to immediately know something and act on it, like when there’s <strong>an error</strong> in submitting information in a form, or something more serious like a <strong>session timeout</strong> or a <strong>security alert</strong>.</li>
</ul>
<p>Assertive notifications are very disruptive and should be limited to a few use cases where the messages are critical to the user and require their immediate attention. Otherwise, they may disorient users or cause them not to complete their current task.</p>
<ul>
<li><code>polite</code> on the other hand, is more… polite. It indicates that the screen reader <strong>should wait until the user is idle</strong> (such as when the screen reader has finished reading the current sentence, or when the user pauses typing) before presenting updates to them. Polite regions do not interrupt the user’s current task. They are more suitable for things like success messages, feeds, chat logs, and loading indicators, for example.</li>
</ul>
<p><code>aria-live="off"</code> is the assumed default value for all elements. It indicates that updates to the element should not be presented to the user <strong>unless the user is currently focused on that region</strong>.</p>
<p>So creating a live region is literally as simple as declaring the <code>aria-live</code> on an element.</p>
<p>Here is an example where I have a <code><div></code> with no <code>aria-live</code> set on it. When you activate the button, the <code><div></code> will get populated with a message via JavaScript; but the screen reader will not announce the update. So the user will not be aware that any content has been added to the <code><div></code> at this point.</p>
<!-- READ THIS IN VIDEO:
When I add `aria-live="assertive"` to it, the `<div>` becomes a live region and is then monitored for updates when they happen. Now when I activate the button again, the screen reader will announce the contents of the message even though focus is not moved to the message. I think that's pretty powerful if you ask me! -->
<p>Try adding <code>aria-live="polite"</code> or <code>aria-live="assertive"</code> to it and then activate the button again. The screen reader will announce the contents of the message even though focus is not moved to the message:</p>
<figure class="wide">
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="RwEjmVy" data-user="SaraSoueidan" data-token="b01e1277ca77766ad54b87615074dedb" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/RwEjmVy/b01e1277ca77766ad54b87615074dedb">
Untitled</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
</figure>
<p>This is pretty powerful!</p>
<p>The live region works even if it is visually-hidden, as long as it is not hidden in a way that removes it from the accessibility tree. (We’ve learned all about choosing an appropriate hiding technique for your content in <a href="https://practical-accessibility.today/chapters/hiding-techniques/">the hiding techniques chapter</a>.)</p>
<p>There are some valid use cases for visually-hidden live regions, but the general rule of thumb is that <strong>if the update or message is visible to all users and the conveyed text is equivalent to the visible text (as is the case for most status messages), then you might as well use the same element for screen reader users that you are using for everyone else</strong>, and designate it as a live region so that all users get the same update.</p>
<p>For example, consider the dummy email example from the previous section again. To convey the same status message to screen reader users, all you would need to do is designate the message container as a live region using the <code>aria-live</code> property. Error notifications are urgent and require the user’s immediate attention, and you want the user to know that an error has occured as soon as possible. As such, the value of <code>aria-live</code> should be <code>assertive</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>assertive<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<aside role="note">
<p>At this point it is important to note that you should place the live region container in the DOM as early as possible and <em>then</em> populate it with the contents of the message using JavaScript when the notification needs to be announced. <strong>This ensures that the live region is monitored for updates before they happen.</strong> Otherwise, the update may not be communicated to screen readers. We will learn more about best practices for implementing live regions later in the chapter.</p>
</aside>
<p>By default, any padding, margin, and border on an element will take up space in the page’s layout even if the element is empty. Since the message container is placed in the DOM before the notification is shown, you will probably want to prevent it from taking up any visual space on the page when it is empty. To do that, you can use the <code>:not(:empty)</code> CSS selector to only apply the visual styles to it when it is <em>not</em> empty (i.e. when the notification is shown).</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">[aria-live="assertive"]:not(:empty)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> .25em 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> maroon<span class="token punctuation">;</span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<aside role="note">
Because it is a best practice to include live region containers in the DOM as early as possible, I always use this CSS selector to visually “hide” my live regions when they are empty.
<p>And yes, yes I know that you can just apply these styles to the notification using a class name that you could add to the container via JavaScript, but why require JS for something so simple that can so easily be accomplished using CSS?</p></aside><p></p>
<p>Here is a live demonstration of this implementation:</p>
<figure class="wide">
<p class="codepen" data-height="648.68701171875" data-default-tab="html,result" data-slug-hash="rNoJRKw" data-user="SaraSoueidan" data-token="5b5ca2a0db044238fc0f9afee3362619" style="height: 648.68701171875px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/rNoJRKw/5b5ca2a0db044238fc0f9afee3362619">
Live region status message in email web app</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<figcaption>
<p>Fire up a screen reader on <a href="https://codepen.io/pen/debug/rNoJRKw/5b5ca2a0db044238fc0f9afee3362619">the debug version of this demo</a> and then activate the ‘Send’ button.</p>
<p>Try removing the <code>:not(:empty)</code> selector from the live region’s ruleset in the to see how it affects the visibility of notification container when it is empty.</p>
</figcaption>
</figure>
<p>A live region does not need to be initially empty.</p>
<p>Here’s another example where I have a list and I’m adding items to the list. I’ve designated the list as a assertive live region using <code>aria-live</code>. So now every time an item is added, the screen reader makes an announcement.</p>
<figure class="wide">
<p class="codepen" data-height="333.708740234375" data-default-tab="html,result" data-slug-hash="QWxpRRY" data-user="SaraSoueidan" data-token="cc48df344e279aa35cee4ea17f5147b8" style="height: 333.708740234375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/QWxpRRY/cc48df344e279aa35cee4ea17f5147b8">
#PracticalA11y: Basic live region</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<p>This means that you can use live regions to communicate different types of updates to an element, such as when content is added to the element or existing content is modified.</p>
<h3 id="live-region-configuration" tabindex="-1">Live region configuration</h3>
<p>ARIA provides three <a href="https://www.w3.org/TR/wai-aria-1.2/#attrs_liveregions">attributes</a> that enable you to ‘configure’ when the screen reader should make an announcement, and what that announcement should contain:</p>
<ul>
<li><code>aria-relevant</code>,</li>
<li><code>aria-atomic</code>, and</li>
<li><code>aria-busy</code>.</li>
</ul>
<p>These attributes are <em>very</em> useful and would enable you to use live regions to communicate different kinds of content updates when they are needed, but unfortunately current browser and screen reader support is inconsistent, so you can’t rely on them in your projects just yet. But we’re still going to get a quick overview of what they do because it will help you better understand the current limitations with ARIA live regions.</p>
<h4 id="aria-relevant%3A-when-should-an-announcement-be-made%3F" tabindex="-1">aria-relevant: when should an announcement be made?</h4>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#aria-relevant">The <code>aria-relevant</code> attribute</a> is used to specify <strong>what type of changes in the live region should trigger an announcement.</strong></p>
<p>For example, should the screen reader make an announcement when a node is <em>added</em> to the region? or when a node is <em>removed</em>? or when the text within an element changes? or maybe when any of these updates happen?</p>
<p><code>aria-relevant</code> accepts a space-separated list of the following values: <code>additions</code>, <code>removals</code>, <code>text</code>, and a single catch-all value: <code>all</code>.</p>
<ul>
<li><code>additions</code> will trigger a notification <strong>when a DOM node is added to the region.</strong></li>
<li><code>removals</code> will trigger a notification <strong>when a DOM node is removed from the region.</strong></li>
<li><code>text</code> will trigger when <strong>text changes happen inside the region</strong>, such as changing a text node inside the region or changing a text alternative for an image inside the region.</li>
<li><code>all</code> is a shorthand for all three options.</li>
</ul>
<p>The default value is <code>additions text</code>, which means that a live region will trigger an announcement when content is added or text is changed within the region.</p>
<p>The <code>removals</code> and <code>all</code> values should be used sparingly. Screen reader users only need to be informed of content removal when its removal represents an important change, such as when a user is removed from the list of active users in a chat room, for example.</p>
<h4 id="aria-atomic%3A-what-is-contained-in-an-announcement%3F" tabindex="-1">aria-atomic: what is contained in an announcement?</h4>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#aria-atomic">The <code>aria-atomic</code> attribute</a> determines what is contained in the announcement. It indicates whether the screen reader should present all or only parts of the changed element based on the change notifications defined by the <code>aria-relevant</code> attribute.</p>
<p>For example, if a piece of text changes inside an element, should the screen reader announce only the changed text? or the entire contents of the live region? If text is <em>added</em> to a live region, should only the newly added text be announced? or should the entire region’s content be announced every time?</p>
<p><code>aria-atomic</code> accepts two values: <code>true</code>, and <code>false</code>.</p>
<ul>
<li>When <code>aria-atomic</code> is <code>false</code>, a screen reader should only announce the parts of the element that have changed. <strong>This is the default value.</strong></li>
<li>When <code>aria-atomic</code> is <code>true</code>, the screen reader should announce the entire contents of the live region when a change happens inside of it. It doesn’t matter what has changed. It’s going to read everything — the entire content of the live region, plus the region’s accessible name, if it has one.</li>
</ul>
<p><code>aria-atomic="true"</code> is useful for when a part of the region changes but you want the entire content to be read because otherwise the updated content may not make much sense on its own. A practical example is a “Now Playing” indicator.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Now Playing:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>[ movie/soundtrack title ]<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>If a playlist of movies or soundtracks is playing while the user performs other tasks on the page, and the name of the soundtrack that is currently playing changes, you can utilize live regions to announce that a new soundtrack is playing.</p>
<p>When the soundtrack changes, the only part of the indicator that gets updated is the soundtrack’s name. But you want the entire sentence to be announced so that the user gets the context they need. You can do that by designating the indicator as an atomic live region (using <code>aria-atomic="true"</code>):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">aria-atomic</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Now Playing:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>[ movie/soundtrack title ]<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now every time the title of the soundtrack or movie changes, the screen reader should announce “Now playing” followed by the name of the soundtrack.</p>
<p>Here is a live demo:</p>
<figure class="wide">
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="poqOVRX" data-user="SaraSoueidan" data-token="971da72a6c94e0bd5352808818565dd8" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/poqOVRX/971da72a6c94e0bd5352808818565dd8">
Untitled</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<figcaption>
<p>Start a screen reader and try out <a href="https://codepen.io/pen/debug/poqOVRX/971da72a6c94e0bd5352808818565dd8">the debug version of the playing indicator</a></p>
</figcaption>
</figure>
<h4 id="aria-busy%3A-please-wait-until-the-changes-are-complete" tabindex="-1">aria-busy: please wait until the changes are complete</h4>
<p>The <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-busy"><code>aria-busy</code> attribute</a> is used to indicate that an element (typically an entire section on the page) is undergoing changes (such as a section loading new content), and that screen readers should therefore <strong>wait until the changes are complete before exposing the content to the user</strong>.</p>
<p>By default, all elements have an <code>aria-busy</code> value of <code>false</code>. Meaning that they are <em>not</em> undergoing changes and screen readers can, therefore, expose their content when the user navigates to them.</p>
<p>To use <code>aria-busy</code>, you would set it to <code>true</code> on an element while the element is undergoing changes, and then flip its value to <code>false</code> when the changes are complete and ready to be exposed or announced to the user.</p>
<p><code>aria-busy</code> can be used on any element that is undergoing changes, even if that element is not a live region.</p>
<p>If you use <code>aria-busy</code> on a live region, the contents of the live region will be announced after <code>aria-busy</code> is set to <code>false</code>. If multiple changes have been made to the element while it was busy, they are announced as a single unit of speech when <code>aria-busy</code> is turned off.</p>
<p>‘<a href="https://www.nngroup.com/articles/skeleton-screens/">Skeleton screens</a>’ in Single Page Applications (SPA) are a practical use case for the <code>aria-busy</code> attribute.</p>
<p>A skeleton screen is a specific type of loading indicator that is shown in lieu of the content of a section being loaded until the content of that section loads. They often provide a wireframe-like visual that mimics the layout of the page and helps users build a mental model of what will be on the page when the content loads.</p>
<figure class="wide">
<picture><source type="image/webp" srcset="https://sarasoueidan.com/assets/images/youtube-skeleton-600w.webp 600w, https://sarasoueidan.com/assets/images/youtube-skeleton-900w.webp 900w, https://sarasoueidan.com/assets/images/youtube-skeleton-1500w.webp 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><source type="image/jpeg" srcset="https://sarasoueidan.com/assets/images/youtube-skeleton-600w.jpeg 600w, https://sarasoueidan.com/assets/images/youtube-skeleton-900w.jpeg 900w, https://sarasoueidan.com/assets/images/youtube-skeleton-1500w.jpeg 1500w" sizes="(max-width: 768px) 100vw, 50vw" /><img alt="Screenshot of the Youtube homepage showing a skeleton screen, where grey rectangular boxes visually represent videos and video descriptions." class="undefined" loading="lazy" decoding="async" src="https://sarasoueidan.com/assets/images/youtube-skeleton-600w.jpeg" width="1500" height="937" /></picture>
</figure>
<p><code>aria-busy</code> can be used to tell screen readers to ignore the section that is currently loading content until the content finishes loading. In that sense, it has a similar effect to <code>aria-hidden</code> — it ‘hides’ the contents of a busy region while it is undergoing changes.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- This section is updating... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-busy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> .. </span><br /><span class="highlight-line"> <span class="token comment"><!-- more content is loading --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>Since the busy section is effectively hidden from screen reader users, you will want to communicate the state of the loading content to screen reader users.</p>
<p>You can do that by using a separate, visually-hidden live region. Using this region, you can communicate to the user that a screen has started loading, and then let them know when the loading is complete. So, when the content is loading, your markup would at a certain moment look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Loading content...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-busy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> .. </span><br /><span class="highlight-line"> <span class="token comment"><!-- more content is loading --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>and then when the content is loaded, flip the value of <code>aria-busy</code> to <code>false</code>, and update the content of the live region:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Content loaded.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-busy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> .. </span><br /><span class="highlight-line"> <span class="token comment"><!-- more content is loading --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<aside role="note">This is an example of when a live region can be used exclusively for notifying screen reader users, but doesn’t need to be rendered visually because there is an alternative visual indicator for sighted users (the skeleton screen, in our case). So you can think of the live region like a text alternative for the skeleton screen in this case.</aside>
<p>Unfortunately, because <code>aria-busy</code> is currently not well-supported across screen reader and browser pairings, most screen readers (except JAWS) will read the contents of the busy region even before it’s done loading, which would result in a sub-optimal experience.</p>
<p>You currently need to work your way around that by hiding the busy region using <code>aria-hidden</code>, and un-hiding it when its contents are done loading.</p>
<p>For a detailed writeup about implementing accessible skeleton screens, check out Adrian Roselli’s article <a href="https://adrianroselli.com/2020/11/more-accessible-skeletons.html">“More Accessible Skeletons”</a>. Adrian provides a solution that doesn’t even require you to use live regions at all, and his article includes <a href="https://codepen.io/aardrian/pen/yLOoOdY">a live demo</a> that you can tinker with and try for yourself.</p>
<h4 id="summary-and-support-landscape" tabindex="-1">Summary and support landscape</h4>
<table class="wide">
<caption>Properties for configuring live region announcements</caption>
<thead>
<tr>
<th scope="col">Value</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<th class="value-name" scope="row"><code>aria-atomic</code></th>
<td class="value-description">
<p><em>What is announced? When you update a live region, should it read all the content again or just the added content?</em></p>
<p>If <code>true</code>: Announce the entire content of the live region, including its label, if present.</p>
<p>If <code>false</code>: announce only the changed content.</p>
</td>
</tr>
<tr>
<th class="value-name" scope="row"><code>aria-relevant</code></th>
<td class="value-description">
<p><em>When is an announcement made? What types of changes to a live region should trigger the announcement? additions? removals? or all?</em></p>
<p>If <code>additions</code>: Trigger an announcement when new elements are added to the accessibility tree of the live region.</p>
<p>If <code>text</code>: Trigger an announcement when text content or a text alternative is added to any descendant in the accessibility tree of the live region.</p>
<p>If <code>removals</code>: Trigger an announcement when an element, text, or text alternative is removed from the accessibility tree of the live region.</p>
<p>If <code>additions text (default)</code>: Equivalent to the combination of <code>additions</code> and <code>text</code>.</p>
<p>If <code>all</code>: Equivalent to the combination of <code>additions removals text</code>.</p>
</td>
</tr>
<tr>
<th class="value-name" scope="row"><code>aria-busy</code></th>
<td class="value-description">Indicates that an entire section on the page is undergoing changes (such as a section loading new content), and you're telling screen readers to <strong>wait until the changes are complete before notifying the user</strong>
<p>If <code>true</code>: The element is being updated.</p>
<p>If <code>false</code>: There are no expected updates for the element.</p>
</td>
</tr>
</tbody>
</table>
<p>As we mentioned earlier, <strong>support for the <code>aria-relevant</code>, <code>aria-atomic</code>, and <code>aria-busy</code> attributes is currently inconsistent across browsers and screen reader pairings.</strong></p>
<p>Paul J. Adam has created <a href="https://pauljadam.com/demos/aria-atomic-relevant.html">a test page that includes test cases for <code>aria-atomic</code> and <code>aria-relevant</code></a> when used on live regions, and has documented support gaps across platforms and screen readers.</p>
<p>So, unfortunately, you can’t rely on these properties in your projects just yet. If you do, many of your content updates may be announced in ways that you did not intend them to be announced, which could result in a sub-optimal user experience.</p>
<h3 id="2.-using-live-region-roles" tabindex="-1">2. Using live region roles</h3>
<p>When you use <code>aria-live</code> to create a live region, the element’s implicit semantics (if it has any) are retained. This means that you can use the appropriate element to represent the component you’re creating, and if the component is getting updated you can then designate it as a live region with the <code>aria-live</code> attribute.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- this list will be treated like any list on the page would be; since it is also designated as being live, any changes that happen to it should be communicated to screen readers and announced to the user --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>My list semantics are important.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>But I want you to know when new list items are added.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>But what if you’re creating a notification or status message that has no semantic HTML element to represent it? For example, there are no semantic elements to represent (and distinguish between) different types of notifications (such as an alert notification or a status message, for example).</p>
<p>While it is fine to use a <code><div aria-live=""></code> for these notifications, it would be ideal if we exposed the nature or type of a notification to the user using appropriate semantics.</p>
<p>ARIA provides <a href="https://www.w3.org/TR/wai-aria-1.2/#live_region_roles">five live regions roles</a> that semantically represent five different types of updates:</p>
<ul>
<li><strong>The <code>alert</code> role:</strong> represents a live region with important, and usually time-sensitive information, such as error notifications.</li>
<li><strong>The <code>status</code> role:</strong> represents a live region whose content is advisory information for the user but is not important enough to justify an alert, often but not necessarily presented as a status bar (such as a status or success message).</li>
<li><strong>The <code>log</code> role:</strong> represents a live region where new information is added <strong>in meaningful order</strong>, and old information may disappear. Examples of logs are chat logs, messaging history, a game log, or an error log. In contrast to other live regions, <strong>in this role there is a relationship between the arrival of new items in the log and the reading order.</strong> The log contains a meaningful sequence and new information is added only to the end of the log, not at arbitrary points.</li>
<li><strong>The <code>marquee</code> role:</strong> represents a live region where non-essential information changes frequently, such as stock tickers. The primary difference between a marquee and a log is that logs usually have a meaningful order or sequence of important content changes.</li>
<li><strong>The <code>timer</code> role:</strong> represents a live region containing a numerical counter which indicates an amount of elapsed time from a start point, or the time remaining until an end point.</li>
</ul>
<p>Live region roles are <strong>pre-configured</strong>. They come with <em>implicit</em> <code>aria-live</code> and <code>aria-atomic</code> values.</p>
<!-- - Elements with the role `alert` have an implicit assertive aria-live value. They also have an implicit `aria-atomic` value of `true`.
- Elements with the role `status` have an implicit `aria-live="polite"` and an implicit `aria-atomic` value of `true`.
-->
<table>
<caption>ARIA live region roles and their implicit <code>aria-live</code> and <code>aria-atomic</code> mappings</caption>
<thead>
<tr>
<th scope="col">Role</th>
<th scope="col"><code>aria-live</code> value</th>
<th scope="col"><code>aria-atomic</code> value</th>
</tr>
</thead>
<tbody>
<tr>
<th class="value-name" scope="row"><code>alert</code></th>
<td class="value-description"><code>assertive</code></td>
<td class="value-description"><code>true</code></td>
</tr>
<tr>
<th class="value-name" scope="row"><code>status</code></th>
<td class="value-description"><code>polite</code></td>
<td class="value-description"><code>true</code></td>
</tr>
<tr>
<th class="value-name" scope="row"><code>log</code></th>
<td class="value-description"><code>polite</code></td>
</tr>
<tr>
<th class="value-name" scope="row"><code>marquee</code></th>
<td class="value-description"><code>off</code></td>
</tr>
<tr>
<th class="value-name" scope="row"><code>timer</code></th>
<td class="value-description"><code>off</code></td>
</tr>
</tbody>
</table>
<p><code>alert</code> and <code>status</code> are the most commonly used live regions roles and have generally good support. The others have specialized uses and have <strong>poor or no support</strong>, and <code>marquee</code> and <code>timer</code> are even <a href="https://github.com/w3c/aria/issues/1104">in danger of being deprecated and removed from the ARIA specification</a>.</p>
<!-- <aside role="note">
If you want to use the other three live region roles, you'd need to work your way around the lack of support.
For example, the `log` live region role was made for UI components like chat interfaces. By using the `log` role on the chat container, the user can be notified of new messages as they appear in the chat while they are composing a new message.
The `log` role is a **non-atomic, polite live region**. So, when a new message is inserted to the chat, only that message should be announced. You don't want the screen reader to announce the entire content of the chat every time a new message arrives. But because the `log` role is not fully supported, you may end up with a live container that reads the entire chat history every time a new message arrives.
One way you can work around the lack of support is to use a separate, visually-hidden (polite) live region. Then when an incoming message arrives, you insert a copy of that message into the live region so that only this message is announced. You will want to do that for every incoming message, inserting them one by one into the visually-hidden live region, and emptying the live region between insertions. This could ensure maximum compatibility on different platforms, and it would also ensure the chat messages are announced as they are intended.
It is important to note at this point that while chat interfaces do update asynchronously, they don't always need to be implemented as live regions. Unless the chat is the main interface on the page, it probably shouldn't be a live region.
</aside> -->
<h4 id="difference-between-using-aria-live-and-live-region-roles" tabindex="-1">Difference between using <code>aria-live</code> and live region roles</h4>
<p>The primary difference between using live region roles and using <code>aria-live</code> on its own is that <strong>live region roles have semantic meaning.</strong> They add explicit <em>semantics</em> to an element (<q>This is an alert</q>, <q>This is a status message</q>, etc.), so some screen readers may announce “alert” before announcing the content of the message.</p>
<p>For example, here is the dummy email app example again. Instead of using <code>aria-live="assertive"</code> on the notification container, I’m using <code>role="alert</code>. Here’s a video comparing how NVDA announces the notification, first when it is designated as a live region using <code>aria-live="assertive"</code>, and the when it is designated as a live region using <code>role="alert"</code>.</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/chapter--live-regions/nvda-live-regions-demo-2.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>
<p>NVDA announces the word “Alert” before announcing the content of the notification when <code>role="alert"</code> is used. You can try it for yourself in <a href="https://cdpn.io/pen/debug/zYyWzmd/5f0c8bc535640bab3cea6bd22f61d5a2">the debug version of the demo using the role attribute</a>.
</p></figcaption><p></p>
</figure>
<p>Here is another example that implements a form success message using <code>role="status"</code>:</p>
<figure class="wide">
<p class="codepen" data-height="482.22021484375" data-default-tab="html,result" data-slug-hash="YzdOYRL" data-user="SaraSoueidan" data-token="270940e995dd0db53a156fd0637d22e0" style="height: 482.22021484375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/YzdOYRL/270940e995dd0db53a156fd0637d22e0">
#PracticalA11y: role status success message</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<p>Another advantage to using a live region role over <code>aria-live</code> is that <strong>live region roles accept an accessible name.</strong></p>
<p>If you use <code>aria-live</code> to create a live region, the implicit semantics of the element you’re using it on will determine whether or not it can have an accessible name. As we learned in <a href="https://practical-accessibility.today/chapters/accName/#not-all-elements-can-have-a-name">the accessible names and descriptions chapter</a>, some elements are name-prohibited. For instance, a <code><div></code> will not consistently expose an accessible name unless you give it a meaningful role. ARIA live region roles provide meaningful roles to the elements they are used on and can therefore accept an accessible name.</p>
<p>When a live region has an accessible name, screen readers include the name of the region in the announcement.</p>
<figure class="wide">
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="qBLMxPM" data-user="SaraSoueidan" data-token="20ae0def0cbc9ec524d376433ae21d37" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/qBLMxPM/20ae0def0cbc9ec524d376433ae21d37">
#PracticalA11y: shopping cart</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<figcaption>
<p>In this example I have a polite live region that contains the number of items in the user’s shopping cart. When the ‘Add to cart’ button is activated, the number of items is updated and the screen reader announces that number. But anouncing the number of items alone doesn’t provide the user with the same context that sighted users get when the shopping cart is visually updated. Ideally, you’d want the screen reader to announce “Shopping cart, 5 items”.</p>
<p>Using <code>aria-labelledby</code>, you can provide an accessible name to the live region (namely: “Shopping Cart”). So now when the number of items is announced, the screen reader announces ‘Shopping cart’ before announcing the number of items it contains.</p>
<p>You can try the live example out for yourself in <a href="https://codepen.io/pen/debug/qBLMxPM/20ae0def0cbc9ec524d376433ae21d37">the debug version of the shopping cart example</a>.</p>
</figcaption>
</figure>
<p>Providing an accessible name to a live region is useful for when you may have multiple updating regions on the page and you want to communicate which region the updates are coming from. A region’s name thus provides the necessary context for each announcement.</p>
<h3 id="3.-using-the-html-output-element" tabindex="-1">3. Using the HTML <code>output</code> element</h3>
<p>HTML provides one native live region element: <code><output></code>.</p>
<p><a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-output-element">By definition</a>, <code><output></code> represents an element into which you can inject the results of a calculation <strong>or the outcome of a user action.</strong> The second part of the definition can be interpreted to mean that the <code><output></code> element can be used to display a feedback message as a result of user interaction (like a toast or status message!).</p>
<p>The <code><output></code> element has implicit live region semantics. It maps to the ARIA <code>status</code> role, which means that it represents a polite live region.</p>
<p><code><output></code> is also a <a href="https://html.spec.whatwg.org/multipage/forms.html#category-label">labelable element</a>, which means that you can give it an accessible name using the <code><label></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ outputID ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>..<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>output</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ outputID ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> .. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>output</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- or --></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ outputID ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ..</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>output</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[ outputID ]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>output</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>A practical use case for the <code><output></code> element is using it to represent the total price of products in a cart on an e-commerce website.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>result<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your total is:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>output</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>result<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>output</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- or --></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>result<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Your total is:</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>output</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>result<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>output</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>When the user updates the number of items in their cart, the total price is updated to reflect the new total price. Wrapping the price in an <code><output></code> element allows it to be announced by the screen reader when it is updated.</p>
<p>Here is a dummy example where the total price is updated based on how many items are chosen in the select dropdown:</p>
<figure class="wide">
<p class="codepen" data-height="432" data-default-tab="html,result" data-slug-hash="QWxwBML" data-user="SaraSoueidan" data-token="47ad4c41a8d8bb83b9bef7d5d235d483" style="height: 432px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/QWxwBML/47ad4c41a8d8bb83b9bef7d5d235d483">
#PracticalA11y: The <output> live region element</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<p>You have probably also noticed that VO with Safari announces the initial total value before it announces the updated total value every time it makes an announcement.</p>
<p>The <code><output></code> element is currently not consistently announced across browser and screen reader pairings. And not all screen readers announce the accessible name of the <code><output></code> when its content is updated. For example, VoiceOver with Safari announces the content of the <code><output></code> element in the example above but it does not announce its accessible name. NVDA with Firefox does not announce the accessible name either. Whereas paired with Chrome, VoiceOver announces the contents of <code><output></code> with its accessible name as it is intended.</p>
<p>There are also other quirks and some inconsistencies in the way <code><output></code> is currently announced across browser and screen reader pairings. Accessibility engineer Scott O’Hara has written <a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">a great article about the <code><output></code> element</a> that I recommend reading if you want to learn more details about <code><output></code> and its quirks. Scott shares the current state of support, as well as suggestions for working around some of the support gaps.</p>
<!-- such as explicitly assigning the `role="status"` to the `<output>` to work around the lack of support in Firefox and Internet Explorer 11: -->
<!-- ```html
<output role="status"></output>
``` -->
<h2 id="summary-and-outro" tabindex="-1">Summary and outro</h2>
<p>So, to quickly sum up:</p>
<ul>
<li>ARIA live regions are a specific type of notification system primarily surfaced for screen reader users.</li>
<li>You can create a live region using the <code>aria-live</code> attribute. The value of the attribute depends on the type and urgency of the updates you’re communicating.</li>
<li>The <code>aria-relevant</code>, <code>aria-atomic</code>, and <code>aria-busy</code> attributes allow you to configure when an announcement should be made and what the announcement should contain. But support for these attributes is currently poor.</li>
<li>ARIA provides five roles that represent five different types of updates. Of these five roles, <code>alert</code> and <code>status</code> have the best support and can be used to represent status messages in web applications.</li>
<li>The <code><output></code> element is currently the only native HTML live region. The <code><output></code> element has a few quirks and some support gaps that, depending on your use case, you may be able to work around today.</li>
</ul>
<p>As you might imagine, the current state of support for live region features and properties limits your uses of live regions quite a bit. Furthermore, the inherent behavior of live regions also makes them unsuitable for certain types of updates. We’re going to elaborate more on this in the second part of this chapter.</p>
<p>Fortunately, for many (if not most!) common UI patterns, there’s often a more robust way to make users aware of content updates when they happen. And for the few instances when you do need to use live regions, you can make them work by following a few implementation best practices. We will discuss all of that in more detail in <a href="https://sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-2">the second part</a> of this chapter.</p>
<br />
<aside role="note">Many thanks to <strong>James Edwards</strong> (<a href="https://twitter.com/siblingpastry">@siblingpastry</a>) for reviewing this chapter.</aside>
Setting up a screen reader testing environment on your computer
2022-11-24T00:00:00Z
https://sarasoueidan.com/blog/testing-environment-setup/
<p>When you’re designing and developing for accessibility, performing manual testing using a screen reader is important to catch and fix accessibility and usability issues that cannot be caught by automated accessibility checkers. You can catch the majority of issues by performing testing <strong>using the screen readers that your users rely on the most.</strong></p>
<p>If you haven’t already, you want to set up a screen reader testing environment on your computer, and invest a little learning time to get acquainted with the most relevant screen reader commands and shortcuts that you will need to perfom basic manual testing with a screen reader on a day-to-day basis.</p>
<p>In this chapter, we will walk through setting up a screen reader testing environment on your computer. We will discuss software options you have to do that (both free and premium), and <strong>what screen reader and browser combinations to test with.</strong> We will also go through enabling accessibility testing on a Mac (which requires a little manual work to do). And finally, we will learn about a few useful features and cheasheets that make testing a little friendlier when you’re just getting started.</p>
<h2 id="macos-vs-windows-screen-readers" tabindex="-1">macOS vs Windows screen readers</h2>
<p>Both Windows and macOS come with screen readers built into them that are available for free. The built-in Windows screen reader is called <strong>Narrator</strong>. The macOS built-in screen reader is <strong>VoiceOver</strong>.</p>
<p>According to <a href="https://webaim.org/projects/screenreadersurvey9/">WebAIM’s screen reader user survey</a>, <strong>more than 90% of screen reader users reported being on Windows</strong>. And according to the same survey, <strong>the two most popular screen readers are <a href="https://www.freedomscientific.com/Products/Blindness/JAWS">JAWS (Job Access With Speech)</a> and <a href="https://www.nvaccess.org/">NVDA (NonVisual Desktop Access)</a></strong> (which are both Windows screen readers), followed by VoiceOver.</p>
<p>If you’re already on Windows or if you own a Windows machine, you’re already halfway through setting up your screen reader-testing environment.</p>
<p>If you’re on a macOS computer, <strong>you shouldn’t test solely with VoiceOver.</strong> It is more opinionated and does not always reflect what the majority of screen reader users experiences.</p>
<!-- If you’re on macOS and have no access to a Windows machine, you have a couple of options to test your work using Windows screen readers. The first option that I recommend is to **set up a virtual Windows machine** on your Mac. -->
<p>If you’re on macOS and you have no access to a Windows machine (whether an actual machine or a virtual one), you can test your work with Windows screen readers using any modern browser instead. We’ll get back to this in another section.</p>
<!-- ## Choose a virtualization environment
There are three popular Virtual Machine software for macOS:
- [VirtualBox](https://www.virtualbox.org/) (Free, open-source)
- [Parallels Desktop](https://www.parallels.com/products/desktop/) (Commercial)
- [VMWare Fusion](https://www.vmware.com/products/fusion.html) (Commercial)
VirtualBox has an advantage over other options in that it is **open-source and available for free.** Parallels Desktop and VMWare Fusion are commercial software.
I personally use Parallels Desktop because it is fast and has a very intuitive user interface. But you can use whichever one you prefer.
Once you have chosen, downloaded and installed a virtualization environment, you can install and run a virtual machine on it.
## Download a Windows Virtual Machine (VM)
Microsoft [provides free Windows 10 Virtual Machines](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/) that you can download and manage locally.
<figure class="wide"><img src="../../assets/images/article--a11y-testing-env/ms-vms.png" alt="Screenshot of the page on the Microsoft website providing Windows virtual machines for download."></figure>
The VM requies a password to login which Microsoft provides along with the installation instructions. The password is `Passw0rd!`.
Once you've downloaded a VM, you want to install it using the virtualization software you have installed earlier.
After installing, **take a snapshot of the virtual machine** because it will expire after 90 days. Taking a snapshot allows you to roll back in time to that snapshot so you can keep using the virtual machine without interruptions.
To take a snapshop using Parallels desktop:
1. Go to "**Actions**" > "**Take Snapshot**"
<figure class="wide"><img src="../../assets/images/article--a11y-testing-env/vm-snapshot-step-1.png" alt="Screenshot of MSEdge - Win10 virtual machine desktop showing the Take Snapshopt option in the Actions menu."></figure>
2. Give your snapshot a name and an optional description.
<figure class="wide"><img src="../../assets/images/article--a11y-testing-env/vm-snapshot-step-2.png" alt="Screenshot of the Snapshot settings dialog. The dialog contains two fields: Snapshot name, and Description. I have named by snapshot 'A11y Env'. In the Description field, describe your setup. I tend to mention what apps I have setup and what the environment is set up for."></figure>
<aside role="note">
If you're not using Parallels, follow the instructions to take a snapshot in the virtualization environment you chose. Marcus Herrmann wrote [a short and practical step-by-step guide](https://marcus.io/blog/checking-whcm-on-mac) to installing a Windows 10 VM on VirtualBox that I recommend checking out for instructions on how to get set up in VirtualBox.
</aside>
With your Windows VM set up and ready to go, you can now start installing the screen readers you want to test on. -->
<h2 id="setting-up-windows-screen-readers" tabindex="-1">Setting up Windows screen readers</h2>
<p>JAWS is the most popular and feature-rich screen reader. a JAWS license isn’t free and is faily expensive. But you can still use it to perform testing for your work. <strong>JAWS will run in full in demo mode for 40 minutes at a time, until it is activated on your computer.</strong> While this is a limitation for longer testing sessions, the 40 minutes are usually more than enough to perform basic testing.</p>
<p><strong>NVDA is a feature-rich, <em>free</em> alternative to JAWS</strong>. We will install and set up NVDA in the following sections.</p>
<h3 id="download-nvda-screen-reader-on-windows" tabindex="-1">Download NVDA screen reader on Windows</h3>
<p>Go to the <a href="https://www.nvaccess.org/">NVAccess Web site</a>. Click the Download link. That will take you to the <a href="https://www.nvaccess.org/download/">NVDA download page</a>.</p>
<p>NVDA is available for free, but <a href="https://www.nvaccess.org/support-us/#donation-support">a donation</a> is strongly encouraged. <!-- For demoing purposes, I will skip the donation this time. --></p>
<p>Click the <strong>Download</strong> button. Wait for NVDA to download. And go through the installation wizard when it’s done. <!-- (NVDA is already installed on my VM so I will skip the installation now.) --></p>
<h4 id="visualize-nvda%E2%80%99s-current-focus-target-with-visual-highlight" tabindex="-1">Visualize NVDA’s current focus target with Visual Highlight</h4>
<p>To make testing with NVDA more convenient (especially if you’re new to screen reader testing), I recommend enabling NVDA’s Visual Highlight feature.</p>
<p>To enable it, go to <strong>Preferences</strong> > <strong>Settings</strong> > <strong>Vision</strong> > <strong>Visual Highlight</strong>, and check the <strong>Enable Highlighting</strong> option.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/nvda-visual-highlight.png" alt="Screenshot of the Vision panel in NVDA settings. In the Vision panel, the 'Enable Highlighting' option is checked in the Visual Highlight group." /></figure>
<p>What this does is it shows a focus highlight around the element that NVDA is currently focused on — whether it’s in a webpage or anywhere on your system. This feature is useful for partially-sighted screen reader users who want to track the location of the NVDA navigator object and the currently-focused element. Seeing where the screen reader’s current focus is at is also helpful for <em>you</em> when you’re performing testing, especially if you’re recording your screen for an educational video, for example. ^^</p>
<h4 id="enable-nvda-speech-viewer" tabindex="-1">Enable NVDA speech viewer</h4>
<p>Another helpful feature you can enable is the <strong>NVDA Speech Viewer</strong> log window.</p>
<p>Click the NVDA icon in your taskbar (on the bottom right of your screen by default), and go to <strong>NVDA</strong> > <strong>Tools</strong> and enable <strong>Speech Viewer</strong>. You also have the option to open the speech viewer log window by default on NVDA startup.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/nvda-speech-viewer.png" alt="Screenshot of the NVDA taskbar menu, with the speech viewer option enabled in the submenu." /></figure>
<p>The speech viewer log window contains the text that NVDA speaks, which can be helpful when you’re just getting started with screen reader testing. Just keep in mind that its usefulness of sometimes limited because <a href="https://adrianroselli.com/2020/08/speech-viewer-logs-of-lies.html">the log often does not fully represent what is announced</a>.</p>
<h4 id="setup-keyboard-layout-for-testing-with-nvda-on-a-mac" tabindex="-1">Setup keyboard layout for testing with NVDA on a Mac</h4>
<p>If you’re on a Mac, go to <strong>NVDA</strong> > <strong>Preferences</strong> > <strong>Settings</strong> > <strong>Keyboard</strong> and Choose “<strong>Laptop</strong>” Keyboard layout instead of the default Desktop option. The desktop layout relies on many keys which do not exist on some Mac keyboards. You can also set this preference in NVDA’s start popup menu.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/nvda-keyboard-layout.png" alt="Screenshot of the NVDA settings pane." /></figure>
<aside role="note">If you're using JAWS, there is a similar option in the JAWS startup wizard to choose the Laptop keyboard layout instead of the default Desktop layout.</aside>
<!-- <figure class="wide"><img src="../../assets/images/article--a11y-testing-env/jaws-laptop-keyboard-layout.png" alt="Screenshot of the JAWS startup wizard."></figure> -->
<h3 id="map-the-insert-key-to-another-key-on-mac" tabindex="-1">Map the Insert key to another key on Mac</h3>
<p>The <kbd>insert</kbd> key is the default modifier key used by most screen readers on Windows. If you don’t own an external keyboard that has an <kbd>insert</kbd> key, you might need to use a software work-around to make up for the lack of the <kbd>insert</kbd> key on your keyboard.</p>
<p>NVDA settings include an option to set the <kbd>caps lock</kbd> key as the NVDA modifier key. You can do that if you prefer. I personally prefer to not do that because it interferes with typing when the <kbd>caps lock</kbd> is On.</p>
<p>Alternatively, you can use a software program to map one of your less-used keyboard keys to the missing <kbd>insert</kbd> key. I use <a href="https://karabiner-elements.pqrs.org/">Karabiner Elements</a>.</p>
<h4 id="setting-up-karabiner-elements-on-macos" tabindex="-1">Setting up Karabiner Elements on macOS</h4>
<p>Karabiner is a free app. To use it:</p>
<ol>
<li>Download the app from <a href="https://karabiner-elements.pqrs.org/">the Karabiner Elements Website</a>. <strong>You want to download it on your Mac, not in your virtual machine.</strong></li>
<li>Run through the setup, and make sure to enable access in your <strong>System Preferences</strong> settings if it is blocked by macOS (which it probably will be by default).</li>
<li>Once it is installed and your keyboard is recognized, go to <strong>Simple Modifications</strong>.</li>
<li>Choose the device(s) you want to create a mapping for, and then click <strong>Add Item</strong> to map an unused key to the insert key.</li>
</ol>
<p>In my Karabiner, I mapped the right <kbd>option</kbd> key to the Windows <kbd>insert</kbd> key. And I also mapped the right <kbd>cmd</kbd> key to the <kbd>print screen</kbd> key, which can be used in combination with other keys to quickly turn Windows High Contrast mode On and Off (which is a shortcut that will come in handy in another chapter).</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/karabiner.png" alt="Screenshot of Karabiner’s Simple Modifications panel. In my panel, I have mapped the right_command key to print_screen and the right_option key to insert for all devices." /></figure>
<p>That’s it. Now if you open your VM and fire up a screen reader, you can use the right <kbd>option</kbd> key (or the key of your choice) as a modifier key in place of the <kbd>insert</kbd> key.</p>
<h2 id="virtual-accessibility-testing-in-your-browser" tabindex="-1">Virtual accessibility testing in your browser</h2>
<p>If you’re on macOS and you have no access to a Windows machine, you can test your work with Windows screen readers using any modern browser instead. You can do that using a service called <a href="https://assistivlabs.com/">AssistivLabs</a>.</p>
<p><strong>AssistivLabs is to screen reader testing what BrowserStack is to cross-browser testing.</strong> It <strong>remotely connects you to real assistive technologies</strong> (like NVDA, JAWS, and Windows High Contrast Mode) using any modern web browser.</p>
<p>AssitivLabs <em>currently</em> only offers testing with <em>Windows</em> screen readers and assistive technologies (like Windows High Contrast Mode and Windows Magnifier) for most accounts; testing using macOS assistive technologies will be available in the future.</p>
<p>AssitivLabs is a paid service — it’s not available for free by default. But it is very helpful for when and if getting access to a Windows machine is otherwise not possible.</p>
<aside role="note">
<p>Note that <a href="https://practical-accessibility.today/">Practical Accessibility course</a> enrollees will get <strong>a 6-months unlimited free trial</strong> to Assistivlabs. 🎁</p>
</aside>
<h2 id="enable-keyboard-accessibility-on-a-mac" tabindex="-1">Enable keyboard accessibility on a Mac</h2>
<p>To complement your screen reader, you should enable keyboard accessibility on your Mac.</p>
<p>Keyboard accessibility is not enabled by default on macOS. If you’ve ever tried to tab your way through interactive and focusable elements on webpages and couldn’t, that’s why. (Frustrating, I know.)</p>
<p><strong>You need to manually enable keyboard accessibility on macOS</strong> by going to <strong>System Preferences</strong> > <strong>Keyboard</strong>, and enabling the “<strong>Use keyboard navigation to move focus between controls</strong>” option in the <strong>Shortcuts</strong> tab.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/macos-keyboard-settings.png" alt="Screenshot of the Keyboard preferences pane in macOS System Preferences." /></figure>
<p>On macOS 13+, you’ll go to <strong>System Preferences</strong> > <strong>Keyboard</strong>, and then enable the <strong>Keyboard Navigation</strong> (Use keyboard navigation to move focus between controls. Press the Tab key to move focus forwards and Shift Tab to move focus backwards) option.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/macos-13-keyboard-a11y.png" alt="Screenshot of the Keyboard preferences pane in macOS 13 System Preferences." /></figure>
<p>Once you’ve enabled system-wide keyboard accessibility, you want to also enable keyboard tabbing in Safari.</p>
<p>In Safari, go to <strong>Preferences</strong> > <strong>Advanced</strong>. And enable the “<strong>Press tab to highlight each item on a webpage</strong>” option.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--a11y-testing-env/safari-keyboard-settings.png" alt="Screenshot of the Safari General Preferences pane." /></figure>
<p>Now you can tab your way through webpages as you should.</p>
<aside role="note">If you're on an older version of macOS or you want to enable these settings in Firefox and Chrome, I've added a couple of resources to help you do that in the recommended resources section at the end of the chapter.</aside>
<h2 id="which-browser-and-screen-reader-pairings-should-you-test-on%3F" tabindex="-1">Which browser and screen reader pairings should you test on?</h2>
<p><strong>Screen readers work best when they are paired with the browsers they are the most compatible with.</strong> When performing testing, you can catch most accessibility issues (sometimes even all of them) by pairing each screen reader with the browser it is most commonly used with.</p>
<h3 id="on-macos" tabindex="-1">On macOS</h3>
<p><strong>VoiceOver works best with (and should, therefore, be paired with) Safari.</strong> If you use VoiceOver with Chrome or Firefox, for example, you might get unexpected results because VoiceOver is <strong>optimized</strong> to work with Safari not with other browsers.</p>
<h3 id="on-windows" tabindex="-1">On Windows</h3>
<p><strong>Narrator works best with Edge</strong>, and has difficulty interfacing with other browsers. But Narrator isn’t most users’ first choice.</p>
<p><strong>JAWS</strong> — the most popular of all screen readers on Windows — works best with Chrome and Firefox. When perfoming testing, <strong>pair it with Chrome.</strong></p>
<p><strong>NVDA works best and is commonly paired with Firefox.</strong></p>
<h3 id="mobile-screen-readers" tabindex="-1">Mobile screen readers</h3>
<p>Throughout this course, we will focus mainly on desktop screen reader testing. But you should test your work using mobile screen readers as well.</p>
<p>In <a href="https://webaim.org/projects/screenreadersurvey9/">WebAIM’s ninth screen reader user survey</a>, <strong>90% of respondents reported using a screen reader on a mobile device.</strong> According to WebAIM, this number has increased over the last 12 years. WebAIM also notes that participants with disabilities (91.6%) are more likely to use a mobile screen reader compared to individuals surveyed without disabilities (71.4%). So it is very important that you test your work on mobile to ensure that it works for a large group of screen reader users.</p>
<p>VoiceOver on iOS/iPadOS is the most popular mobile screen reader. VoiceOver comes bundled with iOS/iPadOS. Like its desktop version, you want to <strong>use it in conjunction with mobile Safari.</strong></p>
<p>On Android, <strong>Talkback (the built-in screen reader) is best paired with Chrome.</strong></p>
<h2 id="guides-to-browsing-and-navigating-content-with-a-screen-reader" tabindex="-1">Guides to browsing and navigating content with a screen reader</h2>
<p>Make some time to learn how to navigate and browse web content with each screen reader. It might take some time and feel like a steep learning curve at first, but by doing that you will gain an invaluable skill for your accessibility work.</p>
<p>Here is a list of official user guides that are helpful for getting started:</p>
<ul>
<li><a href="https://www.nvaccess.org/files/nvda/documentation/userGuide.html">NVDA User guide</a>. Most read-only webpages are browsed in NVDA using <a href="https://www.nvaccess.org/files/nvda/documentation/userGuide.html?#BrowseMode">Browse mode</a>.</li>
<li><a href="https://www.freedomscientific.com/Products/software/JAWS/">JAWS documentation</a> (<a href="https://www.freedomscientific.com/training/jaws/hotkeys/#wb">Shortcut to JAWS Hotkeys</a>)</li>
<li><a href="https://support.microsoft.com/en-us/windows/complete-guide-to-narrator-e4397a0d-ef4f-b386-d8ae-c172f109bdb1">Complete guide to Narrator</a> (<a href="https://support.microsoft.com/en-us/windows/appendix-b-narrator-keyboard-commands-and-touch-gestures-8bdab3f4-b3e9-4554-7f28-8b15bd37410a#WindowsVersion=Windows_11">Shortcut to Narrator keyboard commands and touch gestures</a>)</li>
<li><a href="https://help.apple.com/iphone/11/#/iph3e2e415f">VoiceOver Guide</a>
<ul>
<li><a href="https://support.apple.com/en-lb/guide/voiceover/vo27974/10/mac/13.0">Use VoiceOver to browse webpages on Mac</a></li>
<li><a href="https://help.apple.com/voiceover/command-charts/">Apple VoiceOver Command charts</a></li>
</ul>
</li>
<li><a href="https://support.google.com/accessibility/android/topic/10601774?hl=en&ref_topic=3529932">Talkback user guides</a></li>
<li><a href="https://support.apple.com/en-lb/guide/iphone/iph3e2e415f/16.0/ios/16.0">Turn on and practice VoiceOver on iPhone</a></li>
</ul>
<p>And to get a high-level (yet practical) overview of how someone using a screen reader browses the Web, I recommend watching the Browsing with assistive technologies video series by Tetralogical:</p>
<ul>
<li><a href="https://tetralogical.com/blog/2021/09/29/browsing-with-a-desktop-screen-reader/">Browsing with a desktop screen reader</a></li>
<li><a href="https://tetralogical.com/blog/2021/10/05/browsing-with-a-mobile-screen-reader/">Browsing with a mobile screen reader</a></li>
</ul>
<h2 id="screen-reader-keyboard-shortcut-cheatsheets" tabindex="-1">Screen reader keyboard shortcut cheatsheets</h2>
<p>When you’re just getting started with screen reader testing, and you want to test with at least three screen readers across different platforms and devices, it can be difficult to remember all the keyboard shortcuts for each screen reader right away.</p>
<p><a href="https://dequeuniversity.com/screenreaders/">Deque University</a> provides useful screen reader keyboard shortcuts and gestures cheatsheets, that you can either reference on your computer, or print out and have them handy during your testing.</p>
<ul>
<li><a href="https://dequeuniversity.com/screenreaders/survival-guide">Desktop Screen Readers Survival Guide - Basic Keyboard Shortcuts</a></li>
<li><a href="https://dequeuniversity.com/screenreaders/forms-guide">Desktop Screen Readers Forms Guide</a></li>
<li><a href="https://dequeuniversity.com/screenreaders/nvda-keyboard-shortcuts">NVDA keyboard shortcuts</a></li>
<li><a href="https://dequeuniversity.com/screenreaders/jaws-keyboard-shortcuts">JAWS keyboard shortcuts</a></li>
<li><a href="https://dequeuniversity.com/screenreaders/narrator-keyboard-shortcuts">Narrator keyboard shortcuts</a></li>
<li><a href="https://dequeuniversity.com/screenreaders/voiceover-keyboard-shortcuts">VoiceOver keyboard shortcuts</a></li>
</ul>
<h2 id="resources-and-recommended-reading" tabindex="-1">Resources and recommended reading</h2>
<ul>
<li><a href="https://karlgroves.com/efficiency-in-accessibility-testing-or-why-usability-testing-should-be-last/">Efficiency in Accessibility Testing or, Why Usability Testing Should be Last</a></li>
<li><a href="https://marcus.io/blog/checking-whcm-on-mac">Checking Windows High Contrast mode on a Mac for free</a> (inculdes instructions to download and set-up VirtualBox)</li>
<li><a href="https://dequeuniversity.com/mac/windows-screen-readers">Using Windows Screen Readers on a Mac</a></li>
<li><a href="https://webaim.org/projects/screenreadersurvey9/">The WebAIM screen reader user survey</a></li>
<li><a href="https://www.scottohara.me/blog/2014/10/03/link-tabbing-firefox-osx.html">No, tabbing is not broken. Yes, I was confused too.</a></li>
<li><a href="https://www.a11yproject.com/posts/macos-browser-keyboard-navigation/">Browser keyboard navigation in macOS</a></li>
<li><a href="https://www.smashingmagazine.com/2018/09/importance-manual-accessibility-testing/">The Importance Of Manual Accessibility Testing</a></li>
<li><a href="https://adrianroselli.com/2022/11/your-accessibility-claims-are-wrong-unless.html">Your Accessibility Claims Are Wrong, Unless…</a></li>
<li><a href="https://www.accessibility-developer-guide.com/knowledge/screen-readers/relevant-combinations/">Relevant combinations of screen readers and browsers</a></li>
</ul>
<aside role="note">
<p>Hat tip and thanks to <a href="https://sarasoueidan.com/blog/testing-environment-setup/adrianroselli.com/">Adrian Roselli</a> for pointing out that the <a href="https://addons.nvda-project.org/addons/focusHighlight.en.html">Focus Highlight</a> NVDA add-on is no longer necessary since focus highlighting has been built into NVDA 2019. <!-- The Focus Highlight add-on also uses colored outlines to differentiate between the various NVDA browsing modes.--></p>
<p>And thank you to <a href="https://twitter.com/viki53?s=21&t=UArpY22DI5MPDonW7LSFmA">Corentin H.</a> for providing the screenshot of keyboard accessibility preferences on macOS 13.</p>
</aside>
The CSS prefers-color-scheme user query and order of preference
2021-10-03T00:00:00Z
https://sarasoueidan.com/blog/prefers-color-scheme-browser-vs-os/
<div class="deck">I spent some time in <a href="https://reederapp.com/">Reeder app</a> this morning, catching up with RSS and the latest articles published by my favorite blogs.
<p>I was reading Scott O’Hara’s article about <a href="https://www.scottohara.me//note/2021/10/01/detect-high-contrast-and-dark-modes.html">using JavaScript to detect high contrast and dark modes</a>, which includes a small, very useful script to do exactly what the title says. The output of that script at first looked like it was a “false positive”. But some further investigation led me to learn something new about the <code>prefers-color-scheme</code> CSS user query.</p></div><p></p>
<p>Scott’s article includes <a href="https://codepen.io/scottohara/pen/BaZEQOq">a Codepen</a> to demonstrate the output of the script. The script will check and detect if you currently have high contrast mode or dark mode enabled, and will output the result of the check.</p>
<p class="codepen" data-height="600" data-default-tab="result" data-slug-hash="BaZEQOq" data-user="scottohara" style="height: 571px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span><a href="https://codepen.io/scottohara/pen/BaZEQOq">See the pen</a> (<a href="https://codepen.io/scottohara">@scottohara</a>) on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Since JavaScript doesn’t run in Reeder app, I clicked to open the original article on Scott’s Web site. That’s when I saw that the script was reporting that I had dark mode ON, even though I don’t have dark mode enabled on my phone.</p>
<p>Having just recently updated to iOS 15, my first thought that this might be a browser/OS bug or something.</p>
<p>But then it hit me: I <em>do</em> have dark mode enabled… <em>in Reeder app</em>. <small>(Reeder has a nice dark mode which I enjoy reading in.)</small> This instantly led me to question whether the media query was picking up <em>that</em> dark mode, instead of the OS-level preference.</p>
<p>When I opened the article on Scott’s Web site, I opened it in Reeder’s in-app browser. Which means that the script was running in that context when it reported that dark mode was ON.</p>
<p>So to test my assumption further, I opened the article in iOS Safari, which is running in the Light scheme mode (set on the OS-level). The script does not report that dark mode is ON in that context.</p>
<p>In order to confirm this behavior, I checked the results of the test in Reeder app on my Mac, which is running dark mode on OS-level. I toggled the theme in Reeder app between Light and Dark to verify the results. Sure enough, the script detected dark mode ON when the app theme was set to Dark, but not when the app theme was set to Light.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article__prefers-color-scheme/reeder-dark-theme.png" alt="Screenshot of Scott’s article opened in Reeder’s in-app browser. The codepen result says 'high contrast off and dark mode on'. To the left of the image is Reeder’s preferences pane open, showing the current theme set to 'Dark'" />
<figcaption>The <code>prefers-color-scheme</code> media query picks up the dark mode set in the app. Note that dark mode is also enabled on the OS level, but the media query is picking up the color theme from the app context.</figcaption>
</figure>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article__prefers-color-scheme/reeder-light-theme.png" alt="Screenshot of Scott’s article opened in Reeder’s in-app browser. The codepen result only says 'high contrast off', indicating that dark mode is not on. To the left of the image is Reeder’s preferences pane open, showing the current theme set to 'Classic' (which is a light theme)." />
<figcaption>App color theme taking precedence over OS-level theme. Even though dark mode is enabled on the OS level, the <code>prefers-color-scheme</code> media query picks up the light mode set in the app when the app’s theme is the classic light.</figcaption>
</figure>
<p>In an attempt to verify whether this was a bug or a feature, I checked <a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme">the specification</a>. The spec includes these two paragraphs:</p>
<blockquote>
<p>The method by which the user expresses their preference can vary. It might be a system-wide setting exposed by the Operating System, or a setting controlled by the user agent.
[…]
User preferences can also vary by medium. […] UAs are expected to take such variances into consideration so that prefers-color-scheme reflects preferences appropriate to the medium rather than preferences taken out of context.</p>
</blockquote>
<p>That explains it. <strong>UA preference > OS-level preference.</strong> Something to keep in mind for when an “unexpected behavior” happens. A good reminder to always test and check the specifications. Had this not been in the spec, then further investigation might have led to an existing bug report or to the creation of one. Who knows.</p>
<hr />
<p>And <em>that</em> was my first #TIL moment of the day.</p>
<p><strong>Stay curious.</strong></p>
<p>(Oh and also: <strong>RSS is awesome.</strong> Thank you to everyone providing an RSS feed for their content. <em>You</em> are awesome.)</p>
In Quest of Search
2021-09-15T00:00:00Z
https://sarasoueidan.com/blog/in-quest-of-search/
<p>There’s been <a href="https://twitter.com/domenic/status/1437891692926017537?s=20">a recent discussion on Twitter</a> about the idea of adding a new element in HTML that represents a search interface. A search form, basically.</p>
<p>The idea is to create a semantic HTML element for <a href="https://www.w3.org/TR/wai-aria-1.1/#search">the ARIA <code>search</code> role</a>, which represents a landmark region “that contains a collection of items and objects that, as a whole, combine to create a search facility.”</p>
<p>Opinions have been shared in the Twitter thread about whether adding a new HTML element is necessary. Many have argued that it was unnecessary because we can use the ARIA <code>search</code> role and repurpose a <code>form</code> element to create the same semantics. I disagree. And this article is the longer version of <strong>my personal opinion</strong> on the subject.</p>
<h2 id="tl%3Bdr" tabindex="-1">tl;dr</h2>
<p>I do strongly encourage the addition of a new HTML element that represents—and can consequently obviate the use of—the ARIA <code>search</code> landmark role. A search element would provide HTML parity with the ARIA role, and encourage less use of ARIA in favor of native HTML elements.</p>
<p>The suggested element would be syntactic sugar for <code><div role="search"></code> like <code><main></code> is syntactic sugar for <code><div role="main"></code>. This means that it would an HTML sectioning element, not a replacement for another element.</p>
<p>I would choose <code><search></code> as a name for that element. In my mind, <code><search></code> would be to <code>role="search"</code> what <code><nav></code> is to <code>role="navigation"</code>. But any other appropriate name would, of course, also work.</p>
<p>The rest of this article is my reasoning for encouraging the idea of adding a semantic HTML element for search.</p>
<h2 id="html-and-aria-landmark-roles" tabindex="-1">HTML and ARIA landmark roles</h2>
<p>The ARIA specification includes a list of ARIA <strong>roles</strong> that are used to define regions of a page as landmarks:</p>
<ul>
<li><code>banner</code></li>
<li><code>complementary</code></li>
<li><code>contentinfo</code></li>
<li><code>form</code></li>
<li><code>main</code></li>
<li><code>navigation</code></li>
<li><code>region</code></li>
<li><code>search</code></li>
</ul>
<p>HTML currently contains 112 elements. Eight of those elements are <a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/HTML5.html">sectioning elements</a>: <code>main</code>, <code>nav</code>, <code>aside</code>, <code>header</code>, <code>footer</code>, <code>article</code>, <code>section</code>, <code>form</code>.</p>
<p>Seven of these HTML sectioning elements are <a href="https://www.w3.org/TR/core-aam-1.2/">mapped</a> to ARIA landmarks, which are used by assistive technologies (ATs).</p>
<ul>
<li><code>header</code> is the HTML native equivalent for ARIA’s <code>role="banner"</code> (when it is scoped to the <code>body</code> element. See <a href="https://www.w3.org/TR/html-aam-1.0/">HTML Accessibility API Mappings</a> for more information.)</li>
<li><code>footer</code> is the HTML native equivalent for ARIA’s <code>role="complementary"</code> (also in the context of the <code>body</code> element)</li>
<li><code>nav</code> is the HTML native equivalent for ARIA’s <code>role="navigation"</code></li>
<li><code>main</code> is the HTML equivalent for ARIA’s <code>role="main"</code></li>
<li><code>form</code> is the HTML equivalent for ARIA’s <code>role="form"</code></li>
<li><code>aside</code> is the HTML equivalent of ARIA’s <code>role="complementary"</code></li>
<li><code>section</code> is the HTML native equivalent for ARIA’s <code>role="region"</code> (when it has <a href="https://www.w3.org/TR/wai-aria/#dfn-accessible-name">an accessible name</a>)</li>
</ul>
<p>It is because these elements exist that we often don’t need to use ARIA’s equivalent roles <small>(unless we absolutely <em>have</em> to repurpose another element using those roles, or expose an element to ATs when it is outside of its expected context)</small>.</p>
<p>If <code><nav></code> exists, why should a <code><search></code> <small>(or whatever other name it gets)</small> not? If <code><search></code> is to be deemed unnecessary because <code>role="search"</code> exists, wouldn’t this also mean that <code><nav></code> (and other landmark elements) would be considered <em>redundant</em> because <code>role="nav"</code> (and other ARIA roles) exists?</p>
<h2 id="html-and-aria-landmarks%2C-beyond-semantics" tabindex="-1">HTML and ARIA landmarks, beyond semantics</h2>
<p><a href="https://w3c.github.io/aria/#landmark_roles">ARIA landmark roles</a> are roles assigned to regions of a page that are intended as <strong>navigational landmarks</strong>. Using ARIA landmarks (or their equivalent native HTML elements when they exist) is meant to also facilitate user navigation.</p>
<p>From <a href="https://w3c.github.io/aria/">the W3C WAI-ARIA Editor’s Draft</a>:</p>
<blockquote>
<p>Assistive technologies SHOULD enable users to quickly navigate to elements with role search. User agents SHOULD treat elements with role search as navigational landmarks. User agents MAY enable users to quickly navigate to elements with role search.</p>
</blockquote>
<p>When HTML sectioning elements (and/or ARIA landmark roles) are appropriately used on a page, assistive technology users such as screen readers users could use those landmarks to navigate the page more efficiently, allowing them to jump to the area of the page that they want.</p>
<p>For example, if the <code><nav></code> element (or, equivalently, the <code>role="navigation"</code> ARIA role on a qualifying element) is used to wrap a page’s navigation, the navigation shows up in the VoiceOver Rotor on macOS. Similarly, using the <code>main</code> element will make the main section of the page show up in the landmarks menu. The user can then quickly jump straight to the navigation section or to the main content area of the page if they want to, bypassing other regions of the page. This increases the user’s efficiency and improves their navigation experience.</p>
<p>Similarly, when you use <code>role="search"</code> on a <code>form</code> element, that form will show up as a search region in the landmarks menu. The user can then jump to the search form if they need to quickly search for something.</p>
<figure><img src="https://sarasoueidan.com/assets/images/article__search-html-element/search-vo-webaim.png" alt="A screenshot of VoiceOver's Rotor open on the homepage of Webaim.org, showing a search landmark in the Landmarks menu. The screenshot also shows the Web inspector of the page open and a code snippet highlighting the use of role='search' on the form element wrapping the search input field." /><figcaption>The search form on WebAIM's Web site shows up in the Landmarks menu by VoiceOver on macOS because <code>role="search"</code> ARIA role is present on the <code>form</code> element.</figcaption></figure>
<figure><img src="https://sarasoueidan.com/assets/images/article__search-html-element/search-vo-smashing.png" alt="A screenshot of VoiceOver's Rotor open on SmashingMagazine.com, demonstrating the lack of a search landmark in the landmarks menu. The screenshot also shows the Web inspector of the page open and a code snippet highlighting the absence of role='search' on the form element wrapping the search input field." /><figcaption>The search form on Smashing Magazine's Web site is not recognized as a search landmark by VoiceOver on macOS because <code>role="search"</code> ARIA role is absent on the <code>form</code> element.</figcaption></figure>
<div class="note"><em>If HTML sectioning elements are used without understanding the associated landmark structure, assistive technology users will most likely be confused and less efficient in accessing content and interacting with web pages.</em></div>
<h3 id="but-is-a-native-search-landmark-worth-it%3F" tabindex="-1">But is a native search landmark worth it?</h3>
<p>Yes, it is. Search is one of the most common and most used sections of many Web sites. Of course, a “It Depends” is warranted here, too.</p>
<p>Depending on the Web site, search might be the first thing a user looks for and uses on a given site. E-commerce Web sites are a great example of where search forms are essential and heavily used. Educational and documentation sites are another example.</p>
<p>Take MDN, for example. Search is so important and on MDN that the site even includes a Skip Link that enables keyboard users to skip straight to the search
field.</p>
<figure><img src="https://sarasoueidan.com/assets/images/article__search-html-element/mdn-search-skip-link.png" alt="A screenshot the MDN homepage with a 'skip to search' skip link highlighted at the top edge of the page." /></figure>
<p>Now I don’t have any user research data or anything, but I would assume that the skip link was added because of how frequently users reach for the search field to look up documentation about specific topics they’re searching for.</p>
<h2 id="just-because-an-aria-role-exists%2C-it-doesn%E2%80%99t-eliminate-the-usefulness-of-a-native-html-equivalent" tabindex="-1">Just because an ARIA role exists, it doesn’t eliminate the usefulness of a native HTML equivalent</h2>
<p>I’ll just say it again: ust because an ARIA role exists, it doesn’t eliminate the usefulness of a native HTML equivalent.</p>
<h2 id="the-purpose-of-aria" tabindex="-1">The purpose of ARIA</h2>
<p>…is to provide parity with HTML semantics. It is meant to be used to <strong>fill in the gaps</strong> and provide semantic meaning where HTML falls short.</p>
<p>ARIA is <strong>not meant to <em>replace</em> HTML.</strong> If anything, the need to use ARIA as ‘polyfill’ for HTML semantics could be considered as a sign and a constant reminder of the fact that HTML falls short on some semantics that benefit users of assistive technologies. This is due to the lack of native HTML elements that provide the meaning (and sometimes, by extension, the behavior) that these ATs need to convey to their users.</p>
<p>If we can get an HTML element that fills a part of the gap, it’s only going to be a win—no matter how small of a win it might seem.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">> ARIA is not meant to replace HTML<br /><br />this! In fact, I think we might want it to go the other way around, with HTML replacing ARIA bit by bit until its services are no longer required</p>— Hidde (@hdv) <a href="https://twitter.com/hdv/status/1438197503095103494?ref_src=twsrc%5Etfw">September 15, 2021</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="the-first-rule-of-aria" tabindex="-1">The first rule of ARIA</h2>
<p>The first rule of <a href="https://www.w3.org/TR/aria-in-html/">ARIA use in HTML</a> states that you should <strong>avoid using ARIA if there is a native HTML element with the semantics of behavior that you require already built in.</strong> If such an element exists, you should reach for that element instead.</p>
<p>This means that ARIA should be <strong>a second resort, not a first approach.</strong></p>
<p>By providing HTML elements that are implicitly mapped to ARIA roles, we can encourage the use of proper HTML markup to convey semantic meaning, and spread more awareness to help avoid both overuse and misuse of ARIA in general.</p>
<p>If we can get an HTML element that enables us to use ARIA less, then that element should, in my opinion, be a welcomed addition.</p>
<h2 id="outro" tabindex="-1">Outro</h2>
<p>A native search element might feel like a <em>small</em> technical win to many, but the consistency it provides, the HTML semantics gap it fills, and the awareness it could potentially help spread would all make it a useful and welcomed addition.</p>
<p>112 to 113 HTML elements? I hope so.</p>
A guide to designing accessible, WCAG-conformant focus indicators
2021-08-13T00:00:00Z
https://sarasoueidan.com/blog/focus-indicators/
<style>
figure {
margin: 3em 0;
}
figure img {
margin: 0 auto;
}
</style>
<p>Imagine you visit a website and you want to browse it for some content. You want to buy something; or maybe book a flight somewhere. And as you move your cursor onto the page, it suddenly disappears. Your hand may be still on the mouse, and you’re moving the mouse across the screen and across the page; but you can’t see where it is. You may or may not be hovering over a link or a button or any other form control at any moment. But if you <em>are</em> hovering over one, you don’t know which one it is. You could try clicking and then finding out, but you can probably already imagine what a nightmare of an experience you’re about to get into.</p>
<p>Unfortunately, keyboard users experience the Web in a similarly frustrating manner too often. Their equivalent of a mouse cursor is usually hidden on too many websites, making it almost impossible for them to navigate those sites. A keyboard user’s cursor equivalent is the <strong>focus indicator</strong>. By designing and implementing accessible focus indicators, we can make our products accessible to keyboard users, as well as users of assistive technology that works <em>through</em> a keyboard or emulates keyboard functionality, such as Speech control, switch controls, mouth sticks, and head wands, to mention a few.</p>
<h2 id="what-exactly-is-a-focus-indicator%3F" tabindex="-1">What exactly <em>is</em> a focus indicator?</h2>
<p>Keyboard users typically navigate their way through websites by pressing the <kbd>tab</kbd> key. This allows them to jump from one focusable element on the page to another.</p>
<p>Just like mouse users, they need to be able to see where they are on a page as they Tab their way through it, otherwise they won’t be able to identify the elements they are interacting with. That’s what <strong>focus indicators</strong> are for.</p>
<p>A focus indicator is a visual indicator that “highlights” the currently focused element. This visual indicator is commonly presented as an outline around the element. An outline takes the shape of its element, and since every element in CSS is a rectangle, a focus indicator is, therefore, typically a rectangle drawn around an element.</p>
<figure class="wide">
<video class="video-gif" controls="" src="https://sarasoueidan.com/assets/images/article--focus-indicators/mdn-focus-indicator.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>Navigating the Mozilla Developer Network (MDN) website using a keyboard. As you tab through the homepage, you can see a rectangular outline highlighting the currently focused element.</figcaption>
</figure>
<aside role="note">
A focus indicator can also take other forms, but outlines are very common for good reasons.
<p>Outlines have an advantage over other visual indicators (like borders or background colors, for example) in that they can be applied to the element without causing any significant changes to that element. And since an outline is not part of an element’s box model, it does not affect the layout of that element, and will therefore not cause any layout shifts when it is applied. (That’s also why outlines are preferred over borders for visualizing and debugging layouts 💡)</p>
<p>Furthermore, outlines are retained in forced colors modes where background colors, border colors, and box shadows are usually overridden by user and system styles.</p>
</aside>
<p>So a focus indicator allows a keyboard user to see exactly where they are at any given moment. Without it, they wouldn’t know where they are on a page and they wouldn’t be able to navigate the page and operate its controls.</p>
<p><strong>The focus indicator is to keyboard users what the mouse cursor is to mouse users.</strong> And just like you would never want to hide the mouse cursor, you never want to hide the focus indicator.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/laura-focus-slide.png" alt="Laura Carvajal on stage at Fronteers 2018, with a slide on screen behind her that says 'You wouldn't steal their cursor'" />
<figcaption>Laura Carvajal on stage at Fronteers conference 2018.</figcaption></figure>
<p>In fact, <strong>a visible focus indicator</strong> is a requirement for a site to be considered accessible under the Web Content Accessibility Guidelines (WCAG). Removing or hiding focus indicators is a violation of (and will therefore fail) <a href="https://www.w3.org/TR/WCAG22/#focus-visible">Success Criterion (SC) 2.4.7: Focus Visible (Level A)</a>, which states that:</p>
<blockquote>
<p>any keyboard operable user interface has a mode of operation <strong>where the keyboard focus indicator is visible.</strong></p>
</blockquote>
<h2 id="browser-default-focus-indicators" tabindex="-1">Browser default focus indicators</h2>
<p>Browsers provide focus indicators to native interactive elements out of the box, for free. And most of us—if not all—have at some point in time included this CSS snippet in our stylesheets:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:focus</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>to remove those focus indicators applied by the browser.</p>
<p>To meet <strong>Focus Visible</strong>, you should avoid removing the focus indicator provided by the browser <em>unless</em> you are replacing it with your own accessible focus indicator. And I do recommend that you do that. (We’ll elaborate <em>why</em> in this article.)</p>
<p>By not removing the default browser focus indicators, you may meet the requirement of showing a visible focus indicator. But that may not always be enough because a focus indicator needs to be <em>clearly</em> visible to be considered accessible. And browser focus indicators may not always be.</p>
<p>(What’s the benefit of <em>showing</em> a focus indicator that many users can’t <em>see</em>?)</p>
<p>In order for a focus indicator to be clearly visible it needs to have a color contrast that is high enough for users with moderately low vision to be able to discern it.</p>
<p>The default focus indicator’s color contrast is not consistent across browsers.</p>
<figure><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/browser-focus-styles.png" alt="Screenshot showing a blue button on a white background with four different focus indicators as applied by Chrome, Firefox, Edge, and Safari." />
<figcaption>
<p>How Chrome, Firefox, MS Edge, and Safari style their respective focus indicators (at the time of writing) when applied to a blue button on a white background.</p>
</figcaption>
</figure>
<p>Depending on your website’s color palette, the colors of the default browser focus indicators may clash with the colors used on your website, making them difficult (if not impossible) to see. When that happens, you’ll need to <em>override</em> the default focus styles with better, more accessible ones.</p>
<p>In this article, we’re going to learn about the accessibility requirements that your focus indicators need to meet to be considered accessible. Using these requirements, you’ll be able to determine when and why default browser focus indicators don’t meet these requirements, and how you can ensure that your custom indicators will.</p>
<h2 id="wcag-2.1-and-wcag-2.2-focus-indicator-accessibility-requirements" tabindex="-1">WCAG 2.1 and WCAG 2.2 focus indicator accessibility requirements</h2>
<p><a href="https://www.w3.org/TR/WCAG22/#focus-visible">SC 2.4.7: Focus Visible (Level A)</a> requires that a visible focus indicator exists for components that have keyboard focus.</p>
<p>So, the first step in creating accessible focus indicators is to <strong>not hide focus indicators.</strong> 😊</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Do. Not. Do. This. */</span></span><br /><span class="highlight-line"><span class="token selector">*</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token comment">/* This is bad. */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><a href="https://www.w3.org/TR/WCAG21/#non-text-contrast">SC 1.4.11 Non-Text Contrast (Level AA)</a> states that (emphasis mine):</p>
<blockquote>
<p>The visual presentation of the following have a contrast ratio of at least 3:1 against adjacent color(s):</p>
<p>User Interface Components: Visual information required to identify user interface components and states…</p>
</blockquote>
<p>Focus indicators are used to identify a component state (focus). So, according to this criterion, focus indicators <strong>must have a color contrast ratio of at least 3:1 against adjacent colors.</strong></p>
<p>For user interface components, ‘adjacent colors’ means the colors adjacent to the component. For a component’s focus indicator, ‘adjacent colors’ <strong>depends on the position of the focus indicator</strong> within the component. We will learn about this in more detail in another section.</p>
<p>In <a href="https://www.w3.org/TR/WCAG22/">WCAG 2.2</a>, three success criteria are added that define the accessibility of focus indicators depending on their <strong>color, surface area, and visibility</strong>:</p>
<ul>
<li><strong><a href="https://www.w3.org/TR/WCAG22/#focus-not-obscured-minimum">SC 2.4.11 Focus Not Obscured (Minimum) (Level AA)</a></strong>,</li>
<li><strong><a href="https://www.w3.org/TR/WCAG22/#focus-not-obscured-enhanced">SC 2.4.12 Focus Not Obscured (Enhanced) (level AAA)</a></strong></li>
<li><strong><a href="https://www.w3.org/TR/WCAG22/#focus-appearance">SC 2.4.13 Focus Appearance (Level AAA)</a></strong></li>
</ul>
<p>These new criteria aim to ensure that a keyboard focus indicator is <strong>clearly visible and discernible</strong>, and they define the conditions to ensure that.</p>
<p><strong>SC 2.4.13 Focus Appearance</strong> states that:</p>
<blockquote>
<p>When the keyboard focus indicator is visible, an area of the focus indicator meets all the following:</p>
<ul>
<li>is at least as large as the area of a 2 CSS pixel thick perimeter of the unfocused component or sub-component, and</li>
<li>has a contrast ratio of at least 3:1 between the same pixels in the focused and unfocused states.</li>
</ul>
</blockquote>
<p><strong>Focus Appearance</strong> is closely related to <strong>Focus Visible</strong> and <strong>Non-Text Contrast</strong>.</p>
<p><strong>Focus Visible</strong> requires that a visible focus indicator exists while a component has keyboard focus; <strong>Focus Appearance</strong> defines a minimum level of visibility (which we’ll learn about in this article).</p>
<p>Where <strong>Non-Text Contrast</strong> mandates that focus indicators have a color contrast ratio of at least 3:1 against <em>adjacent</em> colors, <strong>Focus Appearance</strong> requires a sufficient change of contrast for the focus indicator area. (We’ll elaborate more on what this means shortly.)</p>
<p>Even though these criteria are at varying conformance levels, they complement each other, and together they ensure that focus indicators are clearly visible and accessible to more people. Since we’re always aiming for usability beyond conformance, we’re going to treat them all equal and learn how to design focus indicators that pass all of them.</p>
<p>The purpose of <strong>Focus Appearance</strong> is to specify <strong>a minimum area</strong> for the focus indicator, as well as a minimum contrast ratio for that area.</p>
<p>The “2px thick perimeter” part of the SC is the <strong>minimum <em>area</em> of the focus indicator that has a 3:1 contrast ratio between the same pixels in the focused and unfocused states.</strong> This does not mean that the focus indicator needs to be a 2px-thick solid outline around the element, only that the indicator needs to be <strong>at least</strong> that large.</p>
<blockquote>
<p>A keyboard focus indicator can take different forms. This Success Criterion encourages the use of a solid outline around the focused user interface component, but allows other types of indicators that are at least as large.
.
— <a href="https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html">Understanding Focus Appearance</a></p>
</blockquote>
<p>Obviously, providing a 2px thick outline around the element would be the simplest way to meet the size requirement. But the focus indicator can take other forms.</p>
<p>In this article, we’re going to understand what it means to be “at least as large as a 2px thick perimeter”, and we’re going to see examples of how you can meet the minimum area requirement without necessarily providing a 2px thick outline as a focus indicator.</p>
<p>We’re going to start by defining two terms that will help you design custom focus indicators while still ensuring they pass the requirements specified in <strong>Focus Appearance</strong>: the <strong>focus indication area</strong>, and the <strong>contrasting area</strong>. These terms were introduced in previous versions of the success criterion and have been edited out in the final wording. But I think they are helpful in understanding and meeting the requirements.</p>
<p>For most of the examples, we’ll be demonstrating and examining the focus indicator requirements when applied to <strong>a blue button set on a white background.</strong></p>
<p>In what follows, we’re going to get a little nerdy!</p>
<h3 id="1.-the-focus-indication-area-and-the-contrasting-area" tabindex="-1">1. The focus indication area and the contrasting area</h3>
<p>When a component changes on focus to include a focus indicator, that change can always be measured as a change of color contrast.</p>
<p>If you add a black outline around the blue button, the change of color between the unfocused and focused states is from white to black. That’s because <strong>the area — the pixels on the screen — that has changed color</strong> in the focused state is the area <em>around</em> the button. That area was initially white, and it changed to black when the button received focus. This area is called <strong>the focus indication area.</strong></p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/focus-indication-area-1.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the blue button with a thick black outline around it. On the right, is a button with the same outline but with a pattern applied to it, indicating that this patterned area is the focus indication area." /></figure>
<p>The focus indication area is the area in square CSS pixels where the change in color between the focused and unfocused states of the component happens.</p>
<p><strong>Focus Appearance</strong> states that “an area of the focus indicator” must have a 3:1 contrast ratio between the unfocused and focused states. That is, an area <em>of the focus indication area</em> (a <em>subset</em> of the focus indication area) must have a 3:1 contrast ratio between the unfocused and focused states.</p>
<p>The area that meets this contrast requirement is called the <strong>the contrasting area</strong>. And <strong>the contrasting area may or may not be equal to the entire focus indication area.</strong></p>
<p>In the previous example, the color change happens from a solid white to a solid black, and the color contrast ratio between the unfocused and focused state (white and black) is <strong>21</strong>:1. So the entire focus indication area meets the minimum contrast requirement. This means that the contrasting area is equal to the entire focus indication area.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/contrasting-area-1.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the blue button with a thick black outline around it. On the right, is a button with the same outline but with a pattern applied to it, indicating that this patterned area is the contrasting area." /></figure>
<p>Similarly, if you add a black outline that is separated from the button, once again, the area that exhibits the change in color is the contrasting area.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/contrasting-area-2.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the blue button with a separated thick black outline. On the right, is a button with the same outline but with a pattern applied to it, indicating that this patterned area is the contrasting area." /></figure>
<p>I like this pattern because it adds some breathing room and helps the focus indicator stand out, making it easier to see.</p>
<aside role="note">
<p>You can offset the outline from the component using the CSS <code>outline-offset</code> property.</p>
</aside>
<p>If you add an outline inside the button itself, the contrasting area then lies inside the button. The change of color is from blue (the button’s background color) to black. The color contrast ratio between the focused and unfocused state is <strong>4.86</strong>:1.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/contrasting-area-3.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the blue button with an inner thick black outline. On the right, is a button with the same outline but with a pattern applied to it, indicating that this patterned area is the contrasting area." /></figure>
<p>If the button changes its background color from blue to black on focus, then the entire button’s background area is the contrasting area, and the color contrast ratio between the focused and unfocused state is once again <strong>4.86</strong>:1.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/contrasting-area-4.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the button in its focused state, having a black background instead of blue. On the right, is a button with with a pattern applied to its background area, indicating that this patterned area is the contrasting area." /></figure>
<p>If the button’s border color changes when it receives focus, then the contrasting area lies along the button’s border:</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/contrasting-area-5.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. In the middle is the button in its focused state, having a border. On the right, is a button with with a pattern applied along its border, indicating that this patterned area is the contrasting area." /></figure>
<p>When the button’s border color or background color change on focus, the contrasting area must have a minimum contrast ratio of 3:1 between the focused and unfocused state to meet <strong>Focus Appearance</strong> contrast requirements. If the contrast change is less than 3:1, the focus indicator not only fails <strong>Focus Appearance</strong>, but it will also fail <a href="https://www.w3.org/WAI/WCAG21/quickref/#use-of-color"><strong>SC 1.4.1 Use of Color (Level A)</strong></a>.</p>
<p>When the focus indicator is a solid color, measuring the color contrast ratio in the contrasting area is straightforward. But color changes may not always be solid. You may want to indicate focus on the button by applying a gradient drop shadow to it. In this case, <strong>only the portion of the gradient with sufficient contrast (larger than 3:1) will be our contrasting area</strong>; the remaining portion that fails will not be a part of it. This is an example of when the contrasting area is smaller than the focus indication area.</p>
<p>You may need to take some spot-checks on the gradient area and establish what area meets the contrast requirement.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/gradient-contrast.jpg" alt="Illustration: On the left is a blue button with a white label in its default, unfocused state. Next to it on the right is the blue button with a translucent black drop shadow as the focus indication area. On the right, is a button with the same drop shadow minus the parts of the drop shadow that don't pass the minimum contrast requirement, indicating that the remaining area (that does pass) is the contrasting area." /></figure>
<p><strong>The greater the change of contrast between the unfocused and focused states, the easier it is for users to see it.</strong></p>
<!-- <hr>
In addition to requiring a minimum contrast in the contrasting area, a focus indicator is required to be large enough to be clearly visible. But how large is large enough? -->
<h3 id="2.-minimum-contrasting-area" tabindex="-1">2. Minimum contrasting area</h3>
<p><strong>The bigger the visible change when the component receives focus, the easier it is to see.</strong> And to ensure that focus indicators have good visibility, <strong>Focus Appearance</strong> requires <strong>a minimum surface area</strong> for the contrasting area: the contrasting area needs at least as large as a 2px thick perimeter around the element.</p>
<p>The simplest way to meet this requirement is to provide a 2px thick outline that <em>encloses</em> the element or component.</p>
<p>To enclose a component means to <strong>solidly</strong> <em>bound</em> or <em>surround</em> the component.</p>
<p>The <a href="https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html">Understanding Focus Appearance page</a> provides two images that demonstrate the difference between an outline that bounds a component, and an outline that surrounds it:</p>
<figure><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/bound-vs-surround.png" alt="Two sets of star ratings. In both sets, the same three stars have been selected, and the focus indicator is visible on the third star. In the first set, the focus indicator is a rectangular outline bounding the star. In the second set, the focus indicator is a star-shaped solid outline that surrounds the star." />
<figcaption>
In the first image, the focus indicator solidly <em>bounds</em> the star. In the second image, the focus indicator (also a solid outline) <em>surrounds</em> the star.
</figcaption>
</figure>
<p>A solid outline around an element is an example of a focus indicator that solidly bounds an element.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 1px solid #000<span class="token punctuation">;</span> <span class="token comment">/* this outline *bounds* the element */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And a solid <strong>2px</strong> outline has an area of at least 2px thick perimeter of the component. (Of course, any outline thicker than 2px will also meet the area requirement.)</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 2px solid #000<span class="token punctuation">;</span> <span class="token comment">/* this outline meets the minimum area requirement */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>If the focus indicator is dashed or dotted (not solid), it no longer “solidly” bounds or surrounds the component (because it is no longer “solid”). And it will also no longer be equal to a 2px thick perimeter of the component.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 2px dashed #000<span class="token punctuation">;</span> <span class="token comment">/* this outline does not the minimum area requirement */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <a href="https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html#dfn-perimeter">perimeter</a> is a <q>continuous line forming the boundary of a shape not including shared pixels, or the minimum bounding box, whichever is shortest.</q></p>
<p>The perimeter calculation for a 2px thick perimeter around an element is: 2×(2×h+2×w) = (4×w + 4×h), where <strong>h</strong> is the height of the element, and <strong>w</strong> is the width. (The calculation is simplified in that it does not include shared pixels.)</p>
<figure><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/2px-perimeter-calculation.jpg" alt="" /></figure>
<p>The perimeter of a circle is <strong>2𝜋r</strong>, where <strong>r</strong> is the radius of that circle. A 2px thick perimeter around a circle is equal to <strong>4𝜋r</strong>.</p>
<p>When the focus indicator is a 2px dashed outline, its area is equal to 2 times the length of the perimeter of the button (or component) <em>minus</em> all the gap spaces introduced between the dashes of the outline. The resulting length is approximately half of the required minimum area.</p>
<p>To meet the minimum area requirement, you can double the thickness of the outline to compensate for the area that is lost in the gaps.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 4px dashed #000<span class="token punctuation">;</span> <span class="token comment">/* this outline meets the minimum area requirement */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Here’s what a dashed outline looks like with 4px thickness. <strong>The thicker the outline, the larger its surface area, and the easier it is to see.</strong></p>
<figure><img src="https://sarasoueidan.com/assets/images/article--focus-indicators/dashed-outline-area.png" alt="Illustration: the blue button on the left with a 2px dashed outline. And on the right is the button with a 4px-thick dashed outline." /></figure>
<p>Now let’s assume, for demonstration purposes, that you’re designing focus styles for a <strong>150px</strong> by <strong>75px</strong> button. A 2px thick perimeter around this button is equal to 4×150px+4×75px = <strong>900px</strong>.</p>
<p>So the focus indicator of this button needs to have a contrasting area of at least 900px.</p>
<p>If you apply an <em>inner</em> outline to the button, this outline is going to be smaller than the perimeter of the button (because the outline’s width and height are shorter than the button’s width and height). And an inner 2px solid outline has an area less than the 2px thick perimeter of the button.</p>
<p>Once again, increasing the thickness of the outline will make up for the area lost by placing the outline inside the button.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/inner-outline-area.jpg" alt="Illustration: On the left: the blue button with a 1px thin inner black outline. In the middle: the blue button with a 2px thick inner outline. On the right: the blue button with a 3px thick inner outline." />
<figcaption>A 130px by 55px 1px-thick solid outline inside the button will have a surface area of 370px. A 2px-thick solid outline's area will be 2×370px = 740px, which is smaller than the required minimum area (900px). A 3px-thick solid outline passes the minimum area requirement with an area of 1110px.</figcaption></figure>
<p>Inner outlines are useful for many elements where providing an outer outline may not be always suitable, like if you have a list of items in a drop-down menu or a drop-down navigation, for example. Other elements with hidden overflow will also benefit from inner outlines.</p>
<p>As we mentioned before, the focus indicator can take other forms, too.</p>
<p>For example, you may provide a focus indicator only on either side of the component. You only need to ensure that the line is thick enough so that its area is at least as large as the perimeter of the item.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/focus-indicator-other-form.jpg" alt="Illustration: a list of items stacked on top of each other. In the focused state, the item has a focus indicator that shows as two lines provided on either side of the item. " />
<figcaption>
In this example, each item in the list is 187px wide and 42px high. A 2px thick perimeter around an item is equal to 4×187+4×42 = 916px.
<p>The focus indicator provided is a 12px thick line along each of the shortest side of an item. The area of each line is 12×42 = 504px. The total area of the focus indicator is 2×504 = 1008px, which is larger than the minimum 916px area. So, this indicator passes minimum area requirements.</p>
</figcaption>
</figure>
<p>The thicker the lines, the larger the contrasting area, the more visible the focus indicator is.</p>
<p>The main goal of the minimum area requirement is to ensure that the focus indicator is easier to see. Whetever the style you choose to indicate focus, the important thing is to ensure that the contrasting area meets the minimum area requirement(s), so that it can be easily seen.</p>
<p>If you provide a gradient focus indicator to an element, you’ll want to calculate the component’s perimeter and ensure the contrasting area within the gradient is at least twice as large as the perimeter.</p>
<h3 id="3.-minimum-contrast-against-adjacent-colors" tabindex="-1">3. Minimum contrast against adjacent colors</h3>
<p>According to <a href="https://www.w3.org/TR/WCAG21/#non-text-contrast">SC 1.4.11 Non-Text Contrast (Level AA)</a>, focus indicators <strong>must have a color contrast ratio of at least 3:1 against adjacent colors.</strong></p>
<p>The <a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html">Understanding Non-Text Contrast</a> page defines ‘adjacent color(s)’ as the “colors adjacent to the component”.</p>
<p>For our button, the adjacent color is the color of the white background around the button.</p>
<p>The adjacent colors for the <em>focus indicator</em> depend on the position of the focus indicator within the component.</p>
<p>The focus indicator can be:</p>
<ul>
<li><strong>outside the component</strong>, in which case it needs to contrast with the background around the component (that’s the indicator’s adjacent color).</li>
</ul>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-outside.jpg" alt="Two blue buttons set on a white background. In the focused state, one of the buttons has a black outline around the button." />
<figcaption>
In this example, the button's focus indicator is a 2px thick black outline that lies outside the button. The adjacent color is the color of the white background around the button. The indicator's black color must have a minimum 3:1 contrast ratio with the white background (which it does).
</figcaption>
</figure>
<ul>
<li><strong>inside the component</strong>, in which case it needs to contrast with the adjacent color(s) <strong>within the component</strong>.</li>
</ul>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-inside.jpg" alt="Two blue buttons set on a white background. In the focused state, one of the buttons has a black outline inside the button." />
<figcaption>
In this example, the button's focus indicator is a 4px thick black outline that lies inside the button. The adjacent color is the background color of the button. The indicator's black color must have a minimum 3:1 contrast ratio with the button's blue background (which in this example it does).
</figcaption>
</figure>
<ul>
<li><strong>along the component’s border</strong>, in which case it needs to contrast <strong>with both the component’s background as well as the background around the component</strong>.</li>
</ul>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-border.jpg" alt="Two blue buttons set on a white background. In the focused state, one of the buttons has a black border as the focus indicator." />
<figcaption>
In this example, the button's focus indicator is a 4px thick black <em>border</em>. The adjacent colors are both the background color of the button, as well as the background color around the button. The indicator's black color must have a minimum 3:1 contrast ratio with the button's blue background as well as with the white background around the button (which in this example it does).
</figcaption>
</figure>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-border-2.jpg" alt="Two black buttons set on a white background. In the focused state, one of the buttons has a black border as the focus indicator." />
<figcaption>
In this example, the button's background color is black. So the black focus indicator does not meet Non-Text Contrast requirements because it has no contrast with the button's black background color, even if it has sufficient contrast against the white background outside the button.
</figcaption>
</figure>
<p>If the focus indicator is <strong>an inner border</strong>, it needs to contrast with the component’s background color, as well as with the component’s border color. Those two are its adjacent colors.</p>
<p>For example, if the button has a blue background, a black border, and the focus indicator is a white inner border, then the white indicator must have a 3:1 contrast ratio against both the blue background and the black border.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-border-inner.jpg" alt="The blue button set on a white background. In the focused state, the button has a thick white inner border as a focus outline." />
</figure>
<ul>
<li><strong>partly inside and partly outside the component</strong>, where either part of the focus indicator can contrast with the adjacent colors.</li>
</ul>
<p>I haven’t seen a focus indicator in the wild that’s partially inside and partially outside an element. But if the indicator <em>is</em> partially inside and partially outside, then it needs to have a 3:1 contrast ratio <strong>either</strong> with the component’s background color, <strong>or</strong> the color outside the component.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/adjacent-colors-partly.jpg" alt="The blue button set on a white background. In the focused state, the button has a thick black outline that's partially inside and partially outside the button." />
<figcaption>The focus indicator in this example is an outline that lies partially inside and partially outside the button. So it needs to contrast either with the white background or with the button's blue background.</figcaption>
</figure>
<h3 id="4.-the-focused-element-cannot-be-obscured" tabindex="-1">4. The focused element cannot be obscured</h3>
<p>The purpose of a focus indicator is to allow the user to see where they are on a page, by making the currently-focused element more prominent.</p>
<p>But what good is a focus indicator if the focused element itself is not visible because it’s hidden off-screen or obscured by other elements on the page?</p>
<p>SC <strong>2.4.11 Focus Not Obscured (Minimum)</strong> states that:</p>
<blockquote>
<p>When a user interface component receives keyboard focus, the component is not entirely hidden due to author-created content.</p>
</blockquote>
<p>In other words, you want to make sure the user can actually see the element or component that they’re focusing on, by making sure it’s not hidden behind other content on the page.</p>
<p>That being said, this criterion requires that the component be <q>not entirely hidden</q>. This does imply that it could be partially hidden, as long as it’s still partially visible.</p>
<p><strong>SC 2.4.12 Focus Not Obscured (Enhanced)</strong> (which is the level-AAA version of this requirement) states that:</p>
<blockquote>
<p>When a user interface component receives keyboard focus, no part of the component is hidden by author-created content.</p>
</blockquote>
<p>When aiming for level-AA conformance, you may get away with partially hiding the focused component, though I can’t imagine where or how that would not be problematic. Try to <strong>always make sure focused component is <em>entirely</em> visible</strong> and not obscured by other content. It’s just better for usability. <strong>Focus Not Obscured (Enhanced)</strong> is one of the level-AAA criteria that are fairly easy to meet, even if you’re not aiming for level-AAA conformance.</p>
<p>When you’re testing your web pages for keyboard accessibility, check that the elements are visible when they receive focus. Make sure they’re not obscured by other elements, like modal dialogs, fly-outs, or fixed components like fixed headers. Conversely, you want to ensure that elements that <em>are</em> intentionally hidden cannot receive focus when they shouldn’t.</p>
<h2 id="examining-(current)-browser-focus-indicators-against-wcag-requirements" tabindex="-1">Examining (current) browser focus indicators against WCAG requirements</h2>
<p>Now that we know the accessibility requirements for focus indicators, we can examine the focus indicators provided by the most popular browsers and measure how well they meet these requirements.</p>
<p><strong>Non-Text Contrast</strong> states that <q>Visual information required to identify user interface components and states [must have a contrast ratio of at least 3:1 against adjacent color(s)], except for inactive components <strong>or where the appearance of the component is determined by the user agent and not modified by the author</strong></q> (emphasis mine).</p>
<p>What this means is that browser focus indicators are exempt from these requirements, <strong>even if they don’t meet them</strong>. That is, the default focus indicators are considered conforming to this criterion, even if they don’t have a 3:1 contrast ratio against adjacent colors — i.e. if they’re not clearly visible.</p>
<p>The default focus indicators are also exempt from <strong>Focus Appearance</strong> requirements as long as <q>the focus indicator and the indicator’s background color are not modified by the author.</q></p>
<p>So, you could get away with showing the default indicator(s) and pass conformance tests, but this does not mean that the focus indicators will be usable by the people who need them. (And they’re often not going to be, as you’re going to see in this section.)</p>
<p>Furthermore, the default focus indicators can be modified and customized by users, and there is no way for you to know what color the indicator is, and whether or not it has sufficient contrast with the colors on your website.</p>
<p>This is yet another instance of “WCAG does not guarantee usability” that we’ve seen quite a few examples of over the course of the previous chapters.</p>
<p>In this section, we’re going to measure just how well the default focus indicators meet or don’t meet the requirements for focus indicators to be clearly visible (and usable).</p>
<p>The purpose of this section is to demonstrate how you might check whether a focus indicator meets the requirements specified in the WCAG criteria we discussed earlier, and to show how and when the default focus indicators would not meet these criteria and, therefore, why you should consider providing more accessible indicators instead.</p>
<p>To determine if the default indicator passes the requirements, you need to check that:</p>
<ul>
<li>it has sufficient contrast with adjacent colors</li>
<li>it has a contrasting area that is at least as large as a 2px thick perimeter of the button</li>
<li>it has sufficient contrast in the contrasting area between the focused and unfocused state</li>
</ul>
<p>If it does not meet either of these, then there’s a good chance the indicator is not easily discernible by people with low vision, and you may want to consider overriding the default focus indicators with more visible ones.</p>
<p>We’re going to test the default focus indicators as they are applied to an unstyled, native button, as well as to three styled buttons: a black one with a grey border, a blue one, and a pink one.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/testing-buttons.png" alt="The four buttons. From left to right: the native unstyled button. It has a white background and a grey border in most browsers. A black button with a grey border. A blue button. And a deepPink button." />
<figcaption>
</figcaption>
</figure>
<p>We’re going to determine the position of the indicator relative to the button (inside, outside, or along the border) first, which we will use to determine the indicator’s adjacent color(s), and to calculate whether or not it has sufficient contrast with those colors.</p>
<p>When the focus indicator is provided outside the button, its adjacent color is the color of the background around the button. The background color is also going to be the color of the focus indicator’s contrasting area in the unfocused state. This means that if the focus indicator has sufficient contrast with the background color, then it will pass both color contrast requirements specified in <strong>Non-Text Contrast</strong> and <strong>Focus Appearance</strong>.</p>
<p>If the focus indicator is provided along the border of the button, then to meet <strong>Non-Text Contrast</strong> requirements, it needs to have sufficient contrast against the button’s background color, as well as the background color of the page. To meet <strong>Focus Visible</strong> <em>and</em> <strong>Use of Color</strong> requirements, it needs to have sufficient contrast between the colors in the unfocused and focused states (i.e. the initial color of the border and the color of the border in the focused state).</p>
<aside role="note">
<p>I use the <a href="https://www.tpgi.com/color-contrast-checker/">Color Contrast Analyzer (CCA)</a> desktop app created by the folks at <strong>TPGi</strong> to check the contrast of colors within my components. It is very handy for doing quick spot checks for pretty much any element on the screen — Web or native.</p>
</aside>
<p><strong>Safari</strong>:</p>
<p>Safari’s default focus indicator color is the accent color defined on the OS-level in <strong>Settings > Appearance > Accent Color</strong>. The default accent color is blue, and so is the default focus indicator color. This color is customizable, so the focus indicator may take any color chosen by the user. On my OS, that color is pink. So the focus indicator is a light pink outline that surrounds the button. The indicator’s adjacent color is the background color surrounding the button.</p>
<p>Depending on the color theme of your website, it may not meet the minimum contrast defined in <strong>Non-Text Contrast</strong> and <strong>Focus Appearance</strong>.</p>
<p>The pink color has a <strong>low contrast ratio</strong> against most background colors. It has a 2.1:1 contrast ratio against the white background. And a 2.1:1 contrast ratio against a black background.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/safari-on-white.gif" alt="Screen recording: navigating through the four buttons set on a white background in Safari shows the default focus indicator around each button when it receives focus." />
<figcaption>Safari's default focus indicator as it appears on our four buttons and a white page background.</figcaption>
</figure>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/safari-on-black.gif" alt="Screen recording: navigating through the four buttons set on a black background in Safari shows the default focus indicator around each button when it receives focus." />
<figcaption>Safari's default focus indicator as it appears on our four buttons and a black page background.</figcaption>
</figure>
<p>Note that the indicator also changes when the button is styled: it is offset from the edges of the button, and it becomes thinner, which makes it even more difficult to discern.</p>
<p><strong>Chrome</strong>:</p>
<p>Chrome’s focus indicator is a pink outline applied along the button’s border. This means that it needs to have sufficient contrast against both the background color outside the button, as well as with the button’s background color.</p>
<p>Depending on the colors used in your components, it may not meet the minimum contrast defined in <strong>Non-Text Contrast</strong>.</p>
<p>For example, the pink outline has a low contrast against the background color of the black button (2.8:1), the blue button (1.7:1), and the pink button (1.4:1), which means that it does not meet <strong>Non-Text Contrast</strong> requirements.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/chrome-on-white.gif" alt="Screen recording: navigating through the four buttons set on a white background in Chrome shows the default focus indicator around each button when it receives focus." />
<figcaption>Chrome's default focus indicator as it appears on our four buttons and a white page background.</figcaption>
</figure>
<p><strong>Edge</strong>:</p>
<p>Edge’s focus indicator is a black outline applied along the button’s border, which means that it needs to have sufficient contrast against both the background color outside the button, as well as with the button’s background color.</p>
<p>Depending on the colors used in your components, it may not meet the minimum contrast defined in <strong>Non-Text Contrast</strong>.</p>
<p>Being black, the indicator provides good contrast against light background colors. But it doesn’t meet the minimum contrast ratio against dark colors.</p>
<p>For example, in our test here, the focus indicator has no contrast against the adjacent black background inside the black button, so it does neet meet the minimum contrast defined in <strong>Non-Text Contrast</strong>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/edge-on-white.gif" alt="Screen recording: navigating through the four buttons set on a white background in Edge shows the default focus indicator around each button when it receives focus." />
<figcaption>Edge's default focus indicator as it appears on our four buttons and a white page background.</figcaption>
</figure>
<p><strong>Firefox</strong>:</p>
<p>Firefox’s current focus indicator is a pink outline that surrounds the button. The indicator’s adjacent color is the background color of the page.</p>
<p>Depending on the background color you use, the pink outline may not meet the minimum contrast defined in <strong>Non-Text Contrast</strong> and <strong>Focus Appearance</strong>.</p>
<p>For example, on a light grey background, the contrast ratio between the pink and the grey is 2:1 (which is less than 3:1). This means that the contrasting area’s contrast ratio does not meet the minimum contrast requirement, and neither does the contrast ratio against adjacent colors.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/firefox-on-grey.gif" alt="Screen recording: navigating through the four buttons set on a light grey background in Firefox shows the default focus indicator around each button when it receives focus." />
<figcaption>Firefox's default focus indicator as it appears on our four buttons and a light grey page background.</figcaption>
</figure>
<hr />
<p>Chrome, Edge, and Firefox apply what looks like <strong>a second outline</strong> — a white outline — around the indicator. You can see it more clearly on darker background colors. For example, here is what it looks like on a dark purple background in Chrome and Edge, respectively:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/chrome-on-dark.gif" alt="Screen recording: navigating through the four buttons set on a dark purplse background in Chrome shows the default focus indicator around each button when it receives focus." />
<figcaption>Chrome's default focus indicator as it appears on our four buttons and a dark purple page background.</figcaption>
</figure>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/article--focus-indicators/edge-on-dark.gif" alt="Screen recording: navigating through the four buttons set on a dark purple background in Edge shows the default focus indicator around each button when it receives focus." />
<figcaption>Edge's default focus indicator as it appears on our four buttons and a dark purple page background.</figcaption>
</figure>
<p>The fact that the focus indicator in these browsers consists of two outlines, and that <strong>both outlines meet the minimum area requirement</strong> (they’re both at least as large as a 2px thick perimeter), then either of these outlines may be sufficient to pass the minimum color contrast requirements.</p>
<p>This white outline lies outside the button, which means that it only needs to contrast with the background color outside the button. It does not need to contrast against the color(s) of the button.</p>
<p>The white color provides sufficient contrast against dark backgrounds, making the indicator clearly visible. This means that if the pink outline in Chrome’s focus indicator does not meet the minimum contrast ratio, for example, the white outline might, thus passing the criteria.</p>
<p>White and pink are still likely going to fail minimum contrast requirements against light background colors, though.</p>
<p>If the white outline were black instead, it would provide sufficient contrast against light background colors.</p>
<p>Now imagine if there were two outlines surrounding a component: a black one <em>and</em> a white one. The black would ensure sufficient contrast against light background colors, and the white would ensure sufficient contrast against dark background colors. This means that the outline would always meet minimum contrast requirements regardless of the colors used on the page.</p>
<p>Edge’s current focus indicator is the closest out of all default indicators that gets close to providing this level of contrast. But the fact that Edge’s indicator is provided as a border means that it should contrast against two adjacent colors, and if these colors are opposite colors (like if one of them is light and the other is dark), it is probably not going to meet the minimum contrast ratio defined in <strong>Non-Text Contrast</strong> against one of them.</p>
<p>But if the black and white outlines both <em>surrounded</em> the button, then they would only need to have a 3:1 contrast ratio against the background color around the button.</p>
<p>Measuring the contrast ratio against only one color means that either the black or the white outline will pass adjacent color contrast requirements. If white doesn’t pass, black will; and vice versa. This makes the combination of black and white the ideal recipe for a more ‘universal’ focus indicator.</p>
<h2 id="a-%E2%80%98universal%E2%80%99-focus-indicator" tabindex="-1">A ‘universal’ focus indicator</h2>
<p>Inspired by Edge’s indicator, you can provide an improved, universal, black-and-white focus indicator for your website that works for most (if not all) focusable elements and provides sufficient contrast against all background colors.</p>
<p>This focus indicator consists of two (or more!) outlines: a white outline and a black outline.</p>
<p>Since we can’t use the <code>outline</code> property to provide two outlines of different colors, we currently need to use a combination of <code>outline</code> and <code>box-shadow</code> to create our desired effect:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:focus-visible</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 3px solid black<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 6px white<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<aside role="note">
<p>Using the <code>:focus-visible</code> selector instead of the <code>:focus</code> selector means that the focus indicator will only be shown when an element reveives <em>keyboard</em> focus. We’ll talk more about this in the next section.</p>
</aside>
<p>The <code>box-shadow</code> declaration creates a ‘solid’ box shadow (with a zero blur radius) around the element. Outlines overlap box-shadows, which means that the 3px solid (black) outline created with the <code>outline</code> property will overlap the box shadow created ‘behind’ the element. So we extend the box-shadow by 3px (so the total width is 6px). This ensures that the visible portion of the (white) shadow is also 3px wide and that it looks like a solid outline.</p>
<p>If most of the buttons or components on your website have dark colors, you may want to flip the order of the black and white outlines so that the white outline creates an even higher contrast with the components:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:focus-visible</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 3px solid white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 6px black<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>In addition to providing sufficient contrast against adjacent colors, this outline has other advantages:</p>
<ol>
<li>Because it is provided outside the component, it increases the visual size of the component when it is shown, making it easier to spot.</li>
<li>The outline provided using the <code>outline</code> property is retained in forced colors modes (like Windows High Contrast Mode) where other visual styles like box-shadows and background colors are overridden by system colors.</li>
</ol>
<p>Here is a live demonstration of this focus indicator. Press the <kbd>tab</kbd> key on your keyboard to navigate to the focusable elements and show the indicator.</p>
<figure class="wide">
<p class="codepen" data-height="717.9006958007812" data-default-tab="result" data-slug-hash="oNJXGrj" data-user="SaraSoueidan" data-token="9a30174ea09fdcb07c0328b1ff469c19" style="height: 717.9006958007812px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/oNJXGrj/9a30174ea09fdcb07c0328b1ff469c19">
#PracticalA11y: Universal focus indicator</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>If you want to take your focus indicators to the next level and make them <em>even more</em> visible, you can use what designer Erik Kroes calls <a href="https://www.erikkroes.nl/blog/the-universal-focus-state/">the “Oreo-focus" indicator</a>. The concept is the same: you use black and white to create a focus indicator, but instead of a white and a black outline, you create two black outlines with a white outline in between (like an Oreo!) This makes the focus indicator even more visible, regardless of what colors you use in your component(s).</p>
<p>You may want to use relative units to size your outlines like Erik does. For demonstration purposes, I’m going to use pixels and increase the outline width to 9px to ensure each black line is 3px thick:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:focus-visible</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 9px double black<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 6px white<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Depending on the color theme of your website, you may also flip the order of the black and white outlines to make them even more visible against your components.</p>
<p>Here is a live demonstration of this focus indicator. Press the <kbd>tab</kbd> key on your keyboard to navigate to the focusable elements and show the indicator.</p>
<figure class="wide">
<p class="codepen" data-height="745.1962280273438" data-default-tab="result" data-slug-hash="wvRaPwP" data-user="SaraSoueidan" data-token="f712035239c58c1c3e7b9e8241c2fbd8" style="height: 745.1962280273438px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/wvRaPwP/f712035239c58c1c3e7b9e8241c2fbd8">
#PracticalA11y: Universal focus indicator</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>You can use either of the outlines we just demonstrated in almost all of your projects. I’ve personally started including them in all my starter CSS files that I reuse across my projects.</p>
<h2 id="showing-the-focus-indicator-only-for-keyboard-users" tabindex="-1">Showing the focus indicator only for keyboard users</h2>
<p>The main argument I usually hear <em>against</em> focus indicators is that they appear even when you don’t want them to—such as when you click on the component with a mouse or tap on it. Designers and stakeholders are usually not very fond of that.</p>
<p>Today, <strong>all modern browsers only show the default focus indicators when they are needed: when navigating the page with a keyboard.</strong> The focus outline doesn’t show up when you click or tap an element; it only shows up when you tab to it with a keyboard.</p>
<p>When you provide your own custom focus indicators, you probably want to do the same and only show them for users who need them. You can do that using the <code>:focus-visible</code> selector.</p>
<p><code>:focus-visible</code> does exactly the same thing <code>:focus</code> does, except that it only applies the focus indicator styles to an element when that element receives <em>keyboard focus</em>.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* shows the universal focus indicator only when elements receive keyboard focus */</span></span><br /><span class="highlight-line"><span class="token selector">:focus-visible</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 9px double black<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 6px white<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now the focus indicator will only be visible to users navigating with a keyboard (or keyboard-like assistive technology), and those who aren’t using a keyboard won’t even know it’s there!</p>
<p><a href="https://caniuse.com/css-focus-visible">Browser support for <code>:focus-visible</code></a> is pretty good — pretty much all modern browsers support it today.</p>
<p>If you (still) need to support Internet Explorer, you can use either use <a href="https://github.com/WICG/focus-visible">the focus-visible JavaScript polyfill</a>, or, if you’re like me and you prefer a progressive enhancement approach that doesn’t rely on JavaScript, you can use <code>:focus-visible</code> as an enhancement <em>in combination with</em> <code>:focus</code>.</p>
<p>In his article about <a href="https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/"><code>:focus-visible</code> and backwards compatibility</a>, Patrick Lauke demonstrates how you can use the <code>:not()</code> negation pseudo-class, and (paradoxically) define styles not for <code>:focus-visible</code>, but to undo <code>:focus</code> styles when it is absent, and then to use <code>:focus-visible</code> if you wanted to provide additional stronger styles for browsers that support it.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">button:focus</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token comment">/* some exciting button focus styles */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">button:focus:not(:focus-visible)</span> <span class="token punctuation">{</span></span><br /> <span class="token comment">/* undo all the above focused button styles<br /><span class="highlight-line"></span><br /><span class="highlight-line"> if the button has focus but the browser wouldn't normally</span><br /><span class="highlight-line"></span><br /> show default focus styles */</span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">button:focus-visible</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token comment">/* some even *more* exciting button focus styles */</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <code>button:focus:not(:focus-visible)</code> part is CSS for “when the button receives focus that is not focus-visible”. That is, “when the button receives focus that is not keyboard focus”… then undo all the <code>:focus</code> styles. Then apply keyboard-only focus styles using <code>button:focus-visible</code>.</p>
<p>As Patrick notes, <q>this works even in browsers that don’t support <code>:focus-visible</code> because although <code>:not()</code> supports pseudo-classes as part of its selector list, browsers will ignore the whole thing when using a pseudo-class they don’t understand/support, meaning the entire <code>button:focus:not(:focus-visible) { ... }</code> block is never applied.</q></p>
<p>I’ll end this section with this paragraph from Patrick’s article (emphasis mine):</p>
<blockquote>
<p>If you care about backwards compatibility (and you should, <strong><em>until you can absolutely guarantee without any doubt that all your users will have a browser that supports</em></strong> <strong><em>:focus-visible</em></strong>), you will always have to either polyfill or use the combination of <code>:focus</code> and <code>:not(:focus-visible)</code> (plus optional even stronger <code>:focus-visible</code>).</p>
</blockquote>
<h2 id="outro" tabindex="-1">Outro</h2>
<p>Focus indicators are one small yet critical addition to your websites that has the ability to improve the usability of your website or application for millions of people who will use it.</p>
<p>If you’re a designer, make a habit to design and include focus indicator styles in your design specs if you don’t already do so. And when you’re designing them, aim for maximum visibility and prioritize usability over aesthetics.</p>
<p>If you don’t want to design custom focus indicators that match your design theme, you may provide the universal focus indicator we showed earlier.</p>
<p>If you’re a developer, include focus styles in your CSS defaults. If you’re working with designers, strike up a discussion about focus styles with them if they don’t already prioritize them in design specs. And use <code>:focus-visible</code> to ensure the focus indicators are only shown for the people that need them.</p>
<h2 id="resources%2C-references-and-further-reading" tabindex="-1">Resources, references and further reading</h2>
<ul>
<li><a href="https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html">Understanding SC 2.4.13 Focus Appearance</a></li>
<li><a href="https://hiddedevries.nl/en/blog/2019-06-06-indicating-focus-to-improve-accessibility">Indicating focus to improve accessibility</a></li>
<li><a href="https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/"><code>:focus-visible</code> and backwards compatibility</a></li>
<li><a href="https://css-tricks.com/focusing-on-focus-styles/">Focusing on Focus Styles</a></li>
<li><a href="https://www.tpgi.com/prevent-focused-elements-from-being-obscured-by-sticky-headers/">Prevent focused elements from being obscured by sticky headers</a></li>
<li><a href="https://www.erikkroes.nl/blog/the-universal-focus-state/">The universal focus state</a></li>
</ul>
<hr />
<p><em>Many thanks to <a href="https://x.com/siblingpastry">James Edwards</a> for his review and feedback on this version of the article, and to <a href="https://x.com/alastc">Alastair Campbell</a> for his review and feedback on the previous version.</em></p>
<!-- - [Focus visible enhanced examples](https://alastairc.uk/tests/wcag22-examples/focus-visible-enh-examples.html) -->
I’m curating the programme for an upcoming accessibility-focussed conference ✨
2021-08-02T00:00:00Z
https://sarasoueidan.com/blog/curating-aaa/
<p class="deck">I’m speaking, MC’ing and curating the programme for <strong>Access All Areas</strong> — an upcoming accessibility-focussed conference. Here’s all about the event, and how you can join us to learn more about accessibility, and maybe even present and share <em>your</em> expertise at the event!</p>
<h2 id="about-the-conference-(what%2C-when%2C-where)" tabindex="-1">About the conference (what, when, where)</h2>
<p><a href="https://webdirections.org/aaa/">Access All Areas</a> (or <em>aaa</em>) is a new online conference—part of a series of focussed conferences created by the folks at <a href="https://webdirections.org/">Web Directions</a>. This conference is focussed on <strong>accessibility for developers</strong>: understanding the different kinds of accessibility issues that our code may create, finding those issues using available auditing and testing tools, as well as techniques and technologies for addressing them.</p>
<p>This inclusive, remote-friendly conference will take place for the first time this year <strong>on October 25th and November 9th</strong>, featuring 12–14 <strong>in-depth talks</strong> about accessibility.</p>
<p>Most online conferences run just like in-person conferences–one or two jam-packed days, of live streamed presentation. <strong>aaa</strong> presentations are pre-recorded and will run over two half-days. It will run for around 4 hours each session (with a bit of downtime built-in). To cater for more audiences across different time zones, <strong>each session will be delivered 3 times, to suit your time zone wherever you are in the world</strong>, so you can participate from the comfort of your own home or office.</p>
<h2 id="the-programme" tabindex="-1">The programme</h2>
<p>There will be <strong>12–14 in-depth talks</strong> by experienced accessibility engineers, focusing on deep dives to different accessibility topics that will help you <strong>level up your day-to-day accessibility practice.</strong></p>
<p>Talks will range between short 20-minute sessions and longer 40-minute sessions, akin to <a href="https://webdirections.org/lazyload/schedule.php">the format of Lazy Load</a>—the performance-focussed event held earlier this year.</p>
<h2 id="want-to-speak-at-the-conference%3F" tabindex="-1">Want to speak at the conference?</h2>
<p>If you’d like to give a talk and share your expertise at <strong>aaa</strong>, you can <a href="https://webdirections.org/aaa/index.php#speakforus">submit a talk idea</a> before August 13th. The CFP will be open until then, and we would <em>love</em> to hear from you!</p>
<p>We’re looking for deep dives on all topics related to developing accessible Web sites and applications, such as:</p>
<ul>
<li>language and technology features (e.g. features of HTML, CSS, Browser APIs, AOM, ARIA), and</li>
<li>practices, or <em>how</em> to use those technologies to create accessible digital experiences (e.g. accessible animations)</li>
</ul>
<p>Since the focus of the conference is accessibility engineering, we’re not looking to focus on some areas that are commonly covered at accessibility focussed conferences. Things like the importance of accessibility, and selling accessibility into an organisation are well covered at other events. We’re also not looking to focus on automated approaches to accessibility.</p>
<p>We’re looking for talks that offer both <strong>focus</strong> and <strong>depth</strong>. So think less about a broad area, and more about a very specific focussed topic you cover in depth, that helps fellow front end devs address challenges when it comes to the accessibility of their sites and applications. Less “everything you need to know about ARIA” and more “ARIA Live Regions for accessible Web Applications”.</p>
<hr />
<p>In addition to being <strong>compensated for your talk</strong>, Web Directions will also <strong>provide you with all the equipment</strong> (camera, lighting, mic, software), <strong>direction, editing and support you need</strong> to create an amazing presentation.</p>
<p>If your talk gets picked, you’ll be getting <strong>an invitation from me</strong> to join the speaker line-up 🥳 as I will be curating the programme for the event… ✨</p>
<h2 id="curating%2C-mc%E2%80%99ing-and-speaking" tabindex="-1">Curating, MC’ing and speaking</h2>
<p>In addition to <strong>giving a talk and MC’ing</strong> the event, I will be <strong>putting together the programme</strong> for the inaugural edition of the event!</p>
<p>If you have an idea you’d like to <a href="https://webdirections.org/aaa/index.php#speakforus">propose</a>, feel free to reach out to me, too!</p>
<p>If you know me, you may already be able to picture what the programme will be like. <strong>I want to curate the programme I want to see.</strong></p>
<p>There are many topics to cover in the context of accessibility (probably too many to cover in one event!). I’d like to make sure the talks cover as many topics as possible, especially <strong>foundational concepts, technologies, and practices</strong> that you should know and can use in your day-to-day work to create more accessible Web sites and applications.</p>
<p>We’ve already gotten a bunch of great talk proposals. And I’ll personally be sending out invitations to some of the most seasoned accessibility folks in the community—including ones I’ve admired and learned a lot from over the years. ❤ So I’m super excited about making this happen!</p>
<p><em>Conference tickets are currently on sale, so you can <a href="https://webdirections.org/aaa/index.php#register">register to access</a> the event, and join us for two days of accessibility goodness in the Fall 🍁</em></p>
A couple of ways to highlight code syntax in Apple Keynote slides
2021-05-23T00:00:00Z
https://sarasoueidan.com/blog/copy-paste-from-vscode-to-keynote/
<p><a href="https://www.apple.com/keynote/">Apple Keynote</a> doesn’t come with code syntax highlighting built in. I <em>really</em> wish it did, and hope that Apple would at some point add this feature, especially considering how many developers and engineers use Keynote to create talk slides.</p>
<p>Seven years ago, I used to take screenshots of code snippets to include them in my presentations. That was <em>very</em> impractical. If I wanted to make changes to the code, I’d have to rewrite it, take a screenshot again, and copy-paste it into Keynote once again. That soon became tiring and highly ineffective.</p>
<p>Today, I know of two quick and easy ways to include “real” syntax highlighted code in Keynote. This post is me dumping my thoughts into my own “<a href="https://www.wizardingworld.com/writing-by-jk-rowling/pensieve">Pensieve</a>”—<em>this blog!</em>—for my future self to reference, and for anyone else who might find these tips useful.</p>
<h2 id="copy-pasting-formatted-code-from-vs-code" tabindex="-1">Copy-pasting formatted code from VS Code</h2>
<p>A few years ago I learned that copying code from <a href="https://code.visualstudio.com/">VS Code</a> to Keynote would preserve the formatting of the pasted code, i.e. I could include real code (text) snippets in my slides with syntax highlighting applied to them. That was a game changer! Shortly after learning that, I installed VS Code on my machine and used it solely for that purpose for quite a while (before I eventually made the complete switch from <a href="https://www.sublimetext.com/">Sublime Text</a>).</p>
<p>To ensure code snippets in my slides have proper and accessible contrast, I typically enable VS Code’s High Contrast theme when I’m writing code for my presentations. (I’ve used the High Contrast theme for daily coding sessions as well for years.)</p>
<p><em>Today</em>, I learned that this VS Code feature is actually a setting (<code>editor.copyWithSyntaxHighlighting</code>) that can be enabled and disabled in VS Code editor settings. I learned this after <a href="https://twitter.com/SaraSoueidan/status/1396493483943337984?s=20">asking</a> for suggestions for alternative ways to highlight code in Keynote after noticing that the code I was copying from VS Code was no longer preserving its formatting. (Thanks to <a href="https://twitter.com/aaronbassett">Aaron Bassett</a> for <a href="https://twitter.com/aaronbassett/status/1396495691929821185?s=20">the tip</a>). Turns out the setting wasn’t enabled after the last fresh install I did.</p>
<p>If you search for <code>copyWithSyntaxHighlighting</code> in VS Code settings, you can find the option that enables/disables it and turn it on/off:</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/vscode-sh.png" alt="The option in VS Code settings that controls whether syntax highlighting should be copied into the clipboard" /></figure>
<p>Alternatively, you can manually set the value to <code>true</code> or <code>false</code> in your user Settings file.</p>
<h2 id="using-the-slides-code-highlighter-web-app" tabindex="-1">Using the Slides Code Highlighter Web app</h2>
<p>If you don’t use VS Code and don’t want to install it for the sole purpose of copy-pasting code snippets into Keynote, you can use <a href="https://romannurik.github.io/SlidesCodeHighlighter/">Slides Code Highlighter</a> tool by <a href="https://twitter.com/romannurik">Roman Nurik</a>, kindly <a href="https://twitter.com/addyosmani/status/1396494912019861508?s=20">shared by Addy Osmani</a> today.</p>
<figure class="wide"><img src="https://sarasoueidan.com/assets/images/slides-code-highlighter.png" alt="The Slides Code Highlighter Web app" /></figure>
<hr />
<p>There are <a href="https://twitter.com/jamie_gaskins/status/1396494700874539013?s=20">other</a> <a href="https://twitter.com/frontstuff_io/status/1396498521356918784?s=20">approaches</a> to go around this, and probably more that I may not be aware of, but the above two approaches are simple and fast enough to work for me for now.</p>
<p>Of course, none of these approaches are as convenient as having built-in syntax highlighting in Keynote. Maybe, someday, Apple will add this feature. Until then, we can only make do with what we got.</p>
Design for reading: tips for optimizing content for Reader modes and reading apps
2021-05-10T00:00:00Z
https://sarasoueidan.com/blog/tips-for-reader-modes/
<p class="deck">Our content will not always look the way we expect it or want it to. Many apps, tools, and environments that people use to browse the Web strip our content of our CSS and apply their own styles to it. And unless we always keep that in mind, we risk creating incomplete or even broken experiences for users of those technologies or tools.</p>
<p>You might be reading this very blog post in a reading app right now, or maybe even listening to it being read out loud to you. In either case, the visual style enhancements I have applied to the content don’t really matter much anymore. My CSS isn’t enhancing your experience. My content—the HTML markup—though, <em>defines</em> your experience and can <a href="https://medium.com/@mandy.michael/building-websites-for-safari-reader-mode-and-other-reading-apps-1562913c86c9">either make it or break it</a>.</p>
<p>Reader modes and Forced Color modes are two common environments where content is typically stripped of our CSS. So a question I think we should constantly be asking ourselves is: <strong>Is our content still understandable without CSS? Or are we relying too much on visual styles to put ideas across?</strong> Does the HTML layer alone provide a decent and sufficient experience to our users? Is our CSS truly the enhancement it is meant to be, <strong>or are we relying too much on our own preferences rather than our users’ to communicate our ideas?</strong></p>
<p>The more I consume content in reading apps, the more I am reminded of the importance and the power of progressive enhancement as a strategy to create resilient and malleable experiences that work for everyone, regardless of how they choose to consume our content. The more we think about the Web <a href="https://adactio.com/articles/16251">in layers</a>, the more robust experiences we can design.</p>
<p>Designing for reading, there are a few things we can learn and do to improve our users’ reading experience (<em>and</em> other users’ experiences as well!):</p>
<h2 id="1.-if-your-idea-requires-css-to-visualize%2C-provide-a-markup-only-alternative." tabindex="-1">1. If your idea requires CSS to visualize, provide a markup-only alternative.</h2>
<p>In my previous article about <a href="https://sarasoueidan.com/blog/horizontal-rules/">creating creative yet accessible horizontal rules</a>, I discussed how I created the birds-on-a-wire horizontal rule style that I use on this Web site.</p>
<p>After the article’s introduction was a horizontal rule, and a paragraph that read:</p>
<blockquote>
<p>If you’re a frequent reader of this blog, then you’ve most likely already seen what a horizontal rule looks like on here. In fact, if you haven’t seen it before, then you might have just done that. The birds-on-a-wire illustration above this very paragraph is a styled <code><hr></code> element.</p>
</blockquote>
<p>The horizontal rule above that specific paragraph was sort of as a “live demo” for the ideas and code discussed in the article. That horizontal rule is styled in my style sheet to look like a bunch of cute birds on a wire.</p>
<img src="https://sarasoueidan.com/assets/images/hr.png" alt="The horizontal rule style I am using on my Web site. It is a vector illustration of a few bird silhouettes standing on a curvey horizontal black wire." />
<p>Knowing that some readers will be reading the article in an RSS app or even their favorite browser’s Reader mode, I provided an alternative or “fallback” piece of content that would only show up for said users when my own CSS is stripped away.</p>
<figure>
<img style="max-width: 400px; display: block; margin: 0 auto;" src="https://sarasoueidan.com/assets/images/reader-only-text.png" alt="Screenshot of what the article reads like in Reeder app with the fallback reader-only text. Above the fallback text is a horizontal line, which is the horizontal rule the text is referring to, and it is rendered as a simple line by Reeder app." />
<figcaption>
<p>In <a href="https://reederapp.com/">Reeder app</a>, horizontal rules are displayed as plain horizontal lines, so my “live demo” would not be accessible to them. As such, I’ve provided alternative text that reads: <em>"If you're reading this article in a reading app or a Web browser's reader mode, then the above horizontal rule will not appear with my custom styles applied, and you'll just see a horizontal line. Here is a picture of what the horizontal rule looks like"</em>. <br /> The paragraph is followed by <em>an image</em> of the birds-on-a-wire horizontal rule style.</p>
<p>Notice how in the screenshot the reader-only piece of text appears in an Italic font style. Using the <code>em</code>phasis HTML element I am able to visually distinguish that part of the article from the surrounding text in the reading app.</p>
</figcaption>
</figure>
<p>Yes, my readers could easily just click through the article’s title and check the horizontal rule styles on my Web site, <strong>but they shouldn’t have to.</strong> They shouldn’t have to switch contexts just to check out a visual style that I can easily provide them with inside their preferred reading environment.</p>
<!-- Our readers should be able to access and consume our content the way they want to. And it is our job to create experiences that are inclusive of as many users as possible. When We build Web sites, we should try to make the content as usable and understandable without CSS as possible. -->
<p>Today, inside my global <code>utilities</code> style sheet, I have a <code>reader-mode-only</code> utility class (that I will probably rename later to something more generic). I use that class to provide alternative content to my readers that would otherwise require my CSS to be understood. That content would always be present by default when my visual styles are not. And when the content is accessed or viewed in an environment where my CSS <em>is</em> used, the <code>reader-mode-only</code> content is hidden with a simple <code>display: none;</code>.</p>
<hr />
<p>The idea here is to <em>improve</em> the user experience. This technique should never be used to shove unwanted content <small>(such as ads, ugh!)</small> down our reader’s throats. I know we’d ideally want people to come visit our sites, but we should respect their choices, especially since our design choices might be questionable—are we presenting too many distractions on the page? are the fonts we’re using unreadable? is the text contrast sufficient? There might be many reasons why our readers might prefer reading our content in Reader modes. Instead of questioning our user’s preferences, we should reconsider our design decisions.</p>
<p><em><strong>Or not!</strong></em> Many people—<em>myself included!</em>—use RSS because it’s a very convenient way to stay up to date with content. The fact that our readers are subscribed to our blogs is but a greater incentive to make sure we provide them with the quality content <em>and experience</em> that they are expecting from us. And if you do have unwanted elements showing up for them when they shouldn’t, make sure you “clean them up”. More on that in the third section.</p>
<h2 id="2.-html-is-for-content.-css-is-for-visual-styles.-keep-content-where-it-belongs%3A-in-the-markup." tabindex="-1">2. HTML is for content. CSS is for visual styles. Keep content where it belongs: in the markup.</h2>
<p>I was reading through <a href="https://www.smashingmagazine.com/2017/12/prototyping-driven-process/">a Smashing Magazine article</a> in Reeder app when I reached what seemed like a sudden change of topic in the middle of the article. What read as part of the article was actually not. What I’m referring to is Smashing Magazine’s custom “ads” (or content callouts) that appear in the middle of each of their articles.</p>
<img src="https://sarasoueidan.com/assets/images/smashing-inline-ad.png" alt="A screenshot of a Smashing Magazine article showing their custom ads in the middle of the article, interrupting the actual article content." />
<p>These custom ads/callouts that highlight Smashing products typically appear in the middle of an article. With Smashing’s CSS applied, this section stands out from the rest of the article, and is preceded with heads-up text that says <em><q>More after jump! Continue reading below! ↓</q></em>. These styles make the callout section distinguishable from the rest of the article, so you can easily skip over that section and continue reading the article without your focus being affected much.</p>
<p>In Safari Reader Mode, however, the experience is not as subtle.</p>
<p>The heads-up text that precedes the callout section <strong>is not real text.</strong> It’s pseudo-content—literally <em>fake</em> content—created and added to the callout section via CSS using a <code>::before</code> pseudo-element.</p>
<img src="https://sarasoueidan.com/assets/images/smashing-pseudo-content.png" alt="The heads-up text appears as a ::before pseudo-element in the Safari devtools and is applied to a using CSS to the callout section." />
<p>And since Reader Mode strips the content of CSS, that piece of text is not rendered, and the callout section no longer looks like a callout section. Instead, it just appears in the middle of the content as part of the content and interrupts the reading flow.</p>
<img src="https://sarasoueidan.com/assets/images/smashing-ad-in-reader-mode.png" alt="Screenshot showing the callout section appearing unstyled in Safari Reader Mode. The section is indistinguishable from the rest of the article content." />
<p class="note">It's worth noting that I noticed this issue while browsing and reading Smashing articles in Reeder app more than a year ago. But the callout section no longer shows up in Reeder app. I'm not sure why that is. That's why I am using Safari Reader mode to demonstrate the issue.</p>
<p>Similarly, the Summary section at the beginning of the article has a <em>“Quick Summary”</em> label that is also generated in CSS. Below the summary is what looks like a horizontal rule separating it from the rest of the content, but that rule is actually just a bottom border style applied to it in CSS. Both the label and the summary disappear in Reader Mode and reading apps.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/smashing-summary-with-css.png" alt="Screenshot of the article summary as it appears in the browser with CSS enabled: it starts with a label that says 'Quick summary' and has a bottom border separating it from the rest of the article content." />
<figcaption>
The Smashing article summary as it appears in the browser with CSS on.</figcaption>
</figure>
<figure>
<img src="https://sarasoueidan.com/assets/images/smashing-summary-in-reader-mode.png" alt="Screenshot of the article summary as it appears in Safari Reader mode: without a label and without a horizontal line separating it from the rest of the article content. Above the summary is also the author bio, which is preceding all of the article content, including the summary." />
<figcaption>
The Smashing article summary as it appears in Safari Reader mode with basic reader mode styles applied to the semantic HTML elements. (I find it intriguing that Safari Reader Mode styles the Summary so that it's italicized, even though there is no semantic markup (such as an <code>em</code> element) to suggest that it should be.)</figcaption>
</figure>
<p>In order to ensure readers always get a proper reading experience, provide real content in HTML, and leave CSS pseudo-elements for decorational content that is not required for the core reading experience.</p>
<hr />
<p>You may have noticed something else happening in the last screenshot: instead of starting with the core content of it, the article starts with the (truncated) author bio. The bio clearly does not belong there, and yet <em>there</em> it is.</p>
<p>Furthermore, the callout section—which is <em>essentially</em> an ad section—also <strong>does not belong in a distraction-free reading environment</strong>. The main reason people use reading apps and Reader modes is to “declutter” the UI so they can have a “quieter” and more focused reading experience. We can do better to improve their experiences even further with just a tiny bit of HTML sparkle. ✨</p>
<h2 id="3.-keep-the-core-experience-clutter--and-distraction-free%3A-hide-non-core-content-by-default-and-show-it-with-css-when-it%E2%80%99s-ok-to-do-so." tabindex="-1">3. Keep the core experience clutter- and distraction-free: Hide non-core content by default and show it with CSS when it’s OK to do so.</h2>
<p>The reason the author bio and callout sections are displayed even in Reader Mode is because they are markup up as part of the main <code><article></code>.</p>
<p>Reader Mode is meant to strip away all parts of a document and only present an article’s text and basic images in a clean and uncluttered format, to improve the user’s reading experience. In order to do that, <strong>Reader looks for content within an <code><article></code> tag, and displays it.</strong></p>
<p><small>(Note that if Safari does’t find content represented within the proper HTML elements, the Reader Mode button doesn’t even show up at all.)</small></p>
<blockquote>
<p>“It’s important to ensure that Reader draws out the key parts of your web page by using semantic markup to reinforce the meaning and purpose of elements in the document […] we indicate which parts of the page are the most important by wrapping it in an <code>article</code> tag.
Specifically, enclosing these header elements inside the <code>article</code> ensure that they all appear in Reader” <br /> – <a href="https://developer.apple.com/videos/play/wwdc2018/239/">https://developer.apple.com/videos/play/wwdc2018/239/</a></p>
</blockquote>
<p>If you View-Source on Smashing Magazine articles, you’ll find that the author bio and the callout section are both included in the main <code><article></code> wrapper. And that’s why they show up in Reader Mode.</p>
<p class="note">I've noticed some inconsistencies in the way Reader Mode displays article content, even within the context of the same Web site. For example, not <em>all</em> content inside an <code>article</code> is displayed in Reader Mode. For example, the article meta information doesn't show up even though it's part of the <code>article</code>. Moreover, The drop caps illustration would sometimes show up and other time it wouldn't. I'm not sure what the reason behind that is, and why some elements show up and others don't.</p>
<p>If you can move content such as author biographies, ads, and other pieces that are not part of the article’s core content <em>out of the <code>article</code></em>, then do so. This will ensure that they don’t show up and clutter the user’s reading experience in Reader Mode and some reading apps.</p>
<p>If you can’t move the clutter out of the article, <strong>you can hide it from Reader Mode using the HTML <code>hidden</code> attribute.</strong></p>
<p>The <code>hidden</code> attribute is the HTML equivalent of CSS’s <code>display: none</code>. You can use it to hide content when CSS is not available. It comes in handy in a lot of scenarios which I will detail in another article. But for now, suffice it to say that by hiding the non-core pieces of content with the <code>hidden</code> attribute, you are making sure that they are hidden even in environments where your CSS doesn’t work and they shouldn’t be present.</p>
<p>Since the <code>hidden</code> attribute has a very low specificity, you can override it in your CSS using a simple <code>display: block/flex/grid/etc.</code>. So, when your reader is accessing and reading your content on, say, your Web site, you can display those elements and style them in a way that does not negatively affect their reading experience.</p>
<p>Flexbox is used to lay out the contents of the Smashing callout section. So the <code>hidden</code> attribute can be applied to the section in the HTML, and it can be overridden in the CSS with a <code>display: flex</code>.</p>
<p>Here is a video recording of me first applying the <code>hidden</code> attribute to the callout section in Safari’s Reader Mode. You can see that the section is removed as soon as the attribute is applied. When the CSS is enabled, I re-apply the <code>hidden</code> attribute, thus hiding the section once again. Then, I apply a <code>display: flex</code> to the section in the CSS panel, which overrides the <code>hidden</code> attribute and shows the section again with the necessary styling applied to it.</p>
<p><video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/videos/smashing-hidden-attribute.mp4" width="100%">
Sorry, your browser doesn’t support embedded videos.
</video></p>
<p>By leveraging the nature and <em>weakness</em> of the <code>hidden</code> attribute—by using it the way it was meant to be used—we can drastically improve our user’s reading experience in their preferred reading environments.</p>
<h2 id="outro" tabindex="-1">Outro</h2>
<p>We have a tendency to always make an assumption about how
our readers are reading our content—probably in the browser, with our fancy styles applied to it.
But if we make a habit out of thinking about the Web in layers and CSS as an enhancement on top of the content
layer, then we can start optimizing and enhancing our users’ reading experiences regardless of their context.</p>
<p>Thinking about the different ways in which users access the Web only shines light on the importance of a progressively enhanced approach to building for the Web. The more we think about the Web in layers and try to improve the experience of one layer before moving to the next, the more resilient experiences we can create. That’s what the essence of progressive enhancement is about.</p>
<p>HTML is powerful as it is. And if marked up right, it forms a solid foundation for a more inclusive and resilient Web. CSS is a fantastic tool that enables us to further enhance an experience on top of HTML, so long as our design decisions don’t break HTML’s inherent semantics and accessibility.</p>
<p>The <code>hidden</code> atribute is one of HTML’s underrated features. I’ll dive more into it and demonstrate more practical use cases for it in an upcoming article. Stay tuned. And thank you for reading.</p>
Component-level art direction with CSS Container Queries
2021-05-09T00:00:00Z
https://sarasoueidan.com/blog/component-level-art-direction-with-container-queries-and-picture/
<p><a href="https://www.oddbird.net/2021/04/05/containerqueries/">Container Queries</a> (CQ) allow us to change the styles of a component so it responds to the size of its nearest container. With CQ, we can change how an element looks based on where on a page it is placed and how much horizontal space it occupies.</p>
<p>A lot about a design pattern could change based on how much space it’s got. So Container Queries can be used to do more than just shift elements around in a layout. <small>(Have you <em>seen</em> <a href="https://twitter.com/jh3yy/status/1390798974756560904">Jhey</a>’s <a href="https://codepen.io/jh3y/pen/qBrEMEe">demo</a> yet?!)</small> But we can only <strong>take full advantage of the potential Container Queries offer us if we can use them <em>everywhere</em> a typical viewport media query can be used</strong>.</p>
<p>Now that we got Container Queries in CSS, I want them in HTML, too. More specifically, I can see them coming in more handy if they were available in HTML to change an image’s <code>source</code> inside <code><picture></code>, just like viewport-based media queries currently are. This would enable us to art-direct images instead of cropping them to fit in a layout.</p>
<hr />
<p>Images are typically designed to adapt to layout changes much like other UI elements are. It’s possibly even more relevant for images to change with a layout than other UI elements because of their nature as <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Replaced_element">replaced elements</a>.</p>
<p>Take the typical card component for example. Assuming the card’s layout doesn’t change, and assuming the image will always be stacked on top of the content, then it’s usually a good idea to change the aspect ratio and orientation of the image between narrow and wide layouts. A landscape photo might look good in a wide layout, but a portrait photo would suit a narrower layout better.</p>
<p>Using a combination of <code>object-fit</code> and <code>aspect-ratio</code> today, we can fit any image inside a container with an aspect ratio that we specify in our CSS. And with container queries at hand, we can create a more flexible implementation, where the image adapts to the size of the card itself, independent of the size of the viewport. This is already a big improvement to the viewport-based implementation.</p>
<p>Using the <code>aspect-ratio</code> property in the following demo I have changed the aspect ratio of the image based on the width of the card component. And <code>object-fit</code> is used to crop and scale the image to fit within the bounds of its container in all three cases (because the default aspect ratio of the image doesn’t match any of the aspect ratios I’ve specified in the CSS).</p>
<figure class="wide">
<p class="codepen" data-height="530" data-theme-id="3617" data-default-tab="result" data-user="SaraSoueidan" data-slug-hash="MWpwvbB" data-editable="true" style="height: 530px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="MWpwvbB">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/MWpwvbB">
MWpwvbB</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<p>Since the demo is using Container Queries which are currently only supported in Chrome Canary behind a flag, you may only see one aspect ratio on all three card variations. Here is a screenshot of what the image looks like for 3 different card widths:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/card-image-aspectratio-objectfit-demo-screenshot.png" alt="A screenshot of the demo using the aspect-ratio and object-fit properties to scale and crop an image in a card component. The aspect ratio of the image responds to the size of the component, and the image is scaled to fit inside its container." />
</figure>
<p>This is a pretty flexible approach for adapting images in card (or other) components. It is particularly useful when we’re dealing with images that a user might upload via a CMS and whose aspect ratios we don’t know. Specifying an aspect ratio in CSS and cropping and scaling an image to fit within it is a solid approach to handle such images.</p>
<p>But cropping and scaling an image is not <em>always</em> the best approach, though, because not all images can or should be cropped. Sometimes cropping an image straight-up ruins it.</p>
<p>Take a portrait of a person, for example. Cropping it to any aspect ratio other than the one it was taken in would ruin it. And yet on the other hand, keeping the default aspect ratio might not be possible because of design and layout requirements (e.g. the image needs to be one in landscape mode in wider layouts otherwise it would be too tall).</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/cropped-portrait-screenshot.png" alt="A screenshot of a demo using the aspect-ratio and object-fit properties to scale and crop a a portrait of an Egyptian child used in a card component. The aspect ratio of the image responds to the size of the component, and the image is scaled to fit inside its container. Unless the aspect ratio is tall enough, the portrait gets cropped in an undesirable way." />
</figure>
<p>So if we have control over what image(s) to use, the better alternative is to use is to change the image entirely in this case. If I were using a portrait of a person on the site, I might want to use two different shots of that person—one for landscape and another for portrait layouts.</p>
<p>To switch between different images we can use the HTML <code><picture></code> element. There are <a href="https://dev.opera.com/articles/responsive-images/">many ways</a> <code><picture></code> can be used to serve responsive images. For art direction, the syntax might look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width: 1024px)<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>desk--wide.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span><br /> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>desk--portrait.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My desk setup<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can see the above code in action on my <a href="https://sarasoueidan.com/desk">Desk page</a>. And here is a screenshot of the Desk page showing two different photos of my desk setup on narrower and wider screens:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/desk-narrow-and-wide.png" alt="A screenshot of the Desk page showing two different photos of my desk setup on narrower and wider screens." />
</figure>
<div class="note"><small><strong>Accessibility considerations:</strong> We currently don't have a way to change the content of the <code>alt</code> attribute when the image source changes. Or, if we look at it differently, we don't have a separate <code>alt</code> attribute for every image <code>source</code> we define in our <code><picture></picture></code>. So if you're changing the <em>content</em> of the image, you need to keep that in mind. If your art-directed image is pure decoration, or if your image layout changes (landscape to portrait) but the core of the content is the same, then this shouldn't be an issue. But keep in mind that you may want to avoid art-direction if the content of the image changes and it doesn't match the description provided in the <code>alt</code> atribute anymore.</small></div>
<p>But here’s the thing: the images you specify in <code><picture></code> respond to the size of the viewport, not the component they are embedded in. The image on my Desk page changes when the size of <em>the viewport</em> hits 1024px. And that’s fine for this particular use case. But if we’re art-directing images inside components, we’ll want them to respond to the component’s size, not the viewport. After all, this is the kind of responsive behavior that we’ve needed for so long and that Container Queries now provides us with. The next step is, hopefully, Container Queries in HTML.</p>
<div class="note">
<strong>Update:</strong> It has been brought to my attention shortly after publishing this article that this features request <a href="https://github.com/w3c/csswg-drafts/issues/5889">has been made and discussed</a> back in January. For more information about the discussion involved in making something like this work (or not!), refer to the <a href="https://github.com/w3c/csswg-drafts/issues/5889">open issue on Github</a>.
</div>
<hr />
<p>There is a way we can currently use Container Queries to art direct images in a component, but those images would need to be <em>embedded in the CSS</em>, as background images.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.card__media</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 2 / 3<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"https://assets.codepen.io/9674/desk--portrait.jpg"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.container</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">container-type</span><span class="token punctuation">:</span> inline-size<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@container</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 480px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector">.card__media</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 16 / 9<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"https://assets.codepen.io/9674/desk--wide.jpg"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>In other words, the images need to be non-semantic, decoration images. If your image is part of your semantic content, it can’t respond to the container query just yet. And of course, since the image is a background image, we’ll need to use the <code>aspet-ratio</code> on the element that image is applied to, otherwise it would collapse. Here’s a live demo again using Container Queries, so it will only currently work in Canary:</p>
<figure class="wide">
<p class="codepen" data-height="523" data-theme-id="3617" data-default-tab="result" data-user="SaraSoueidan" data-slug-hash="qBrdPvV" style="height: 523px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Component-level art-direction using CSS Container Queries">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/qBrdPvV">
Component-level art-direction using CSS Container Queries</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
</figure>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>And a screenshot of the result:</p>
<img src="https://sarasoueidan.com/assets/images/art-directing-bg-images-with-cq.png" alt="a screenshot of the demo using Container Queries to change the (background) image used on a card component." />
<h2 id="final-thoughts" tabindex="-1">Final Thoughts</h2>
<p>We’re only scratching the surface of what Container Queries make possible. I can imagine Container Queries coming handy even in quick prototyping-in-the-browser work, as well as more specific use cases. And I’m sure more smart people will come up with even more use cases as they push the limits of what’s currently possible.</p>
<p>Let’s continue pushing the Web forward.</p>
Not Your Typical Horizontal Rules
2021-03-25T00:00:00Z
https://sarasoueidan.com/blog/horizontal-rules/
<p>The HTML <code><hr></code> element adds a horizontal rule (or line) wherever you place it. A horizontal rule is used to provide a visual break and divide content. Like other HTML elements, horizontal rules can be styled using CSS (and SVG). This means that they don’t have to look like boring, plain horizontal lines. You can get a little creative with them, adding a nice little personal touch to your content and designs.</p>
<p><svg role="separator" style="display: block; max-width: 65%; margin: 4rem auto;" width="500px" height="32px" viewBox="0 0 794 51" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<polygon id="path-1" points="0.907103825 0 798.907104 0 798.907104 364 0.907103825 364"></polygon>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="36257-O0KTX6" transform="translate(0.000000, -212.000000)">
<g id="Group" transform="translate(-4.907104, 171.000000)">
<g id="Group-70">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Clip-67"></g>
<path d="M610.198586,77 C699.286076,77 783.215114,73.4231956 849.907104,64.0083772 L849.765531,63.0342269 C783.152303,72.4392054 699.257163,76.0169938 610.198586,76.0160098 C468.900459,76.0169938 314.600519,67.0124993 195.629099,58.0080049 C136.142392,53.5062497 85.4880646,49.0035105 49.7059121,45.62744 C31.8148358,43.9389128 17.6415541,42.5318068 7.94178925,41.5478166 C3.09140831,41.0548375 -0.640344182,40.6681294 -3.15975068,40.40442 C-5.67816019,40.1407106 -6.98023573,40 -6.98422371,40 L-7.09289617,40.9780863 C-7.08292622,40.9790703 327.520578,77 610.198586,77 Z" id="Fill-68" fill="var(--hr-color)" mask="url(#mask-2)"></path>
</g>
<path d="M204.690104,47.32 C206.259104,42.729 211.015104,44.247 211.564104,45.397 C212.744104,47.867 213.564104,47.459 213.564104,47.459 C213.564104,47.459 211.475104,47.412 210.596104,49.717 C210.211104,50.727 208.816104,56.337 204.832104,58.949 C202.997104,60.153 200.700104,60.071 198.966104,61.194 C197.487104,62.163 196.569104,65.733 194.477104,65.784 C194.528104,65.733 194.579104,65.58 194.630104,65.479 C194.069104,65.835 193.355104,65.632 192.743104,65.632 C192.845104,65.376 192.590104,65.58 192.743104,65.325 C192.641104,65.428 192.641104,65.172 192.539104,65.223 C192.488104,64.764 192.284104,64.458 192.590104,64.101 C192.437104,64.254 192.590104,64.101 192.437104,64.203 C192.131104,63.285 196.633104,60.39 197.092104,57.891 C198.220104,51.746 202.522104,48.379 204.690104,47.32" id="Fill-73" fill="var(--hr-color)"></path>
<path d="M237.461104,48.48 C240.188104,44.985 244.713104,48.584 244.314104,51.397 C248.502104,52.923 251.137104,63.152 251.621104,65.056 C251.621104,65.056 254.867104,70.497 254.733104,74.267 C254.250104,72.998 254.242104,74.368 253.553104,73.169 C253.483104,73.311 252.586104,72.323 252.517104,72.464 C251.439104,71.459 250.904104,67.897 250.103104,67.455 C249.301104,67.012 244.307104,65.92 241.012104,62.94 C237.824104,60.056 237.482104,56.195 237.693104,54.727 C238.377104,49.959 234.689104,50.147 234.689104,50.147 C234.689104,50.147 236.502104,49.709 237.461104,48.48" id="Fill-79" fill="var(--hr-color)"></path>
<path d="M490.329073,53.2234708 C489.681073,52.5704708 487.329073,50.5564708 485.450073,51.0884708 C483.536073,51.6304708 481.516073,54.0154708 481.624073,56.5644708 L481.662073,56.5564708 C481.662073,56.5564708 480.546073,57.4964708 480.276073,57.7454708 C479.575073,58.3664708 475.473073,63.1004708 474.745073,69.2234708 C474.510073,71.1834708 474.352073,73.7664708 474.662073,75.0564708 C474.972073,76.3454708 473.722073,78.5464708 473.020073,79.9774708 C472.696073,80.5984708 469.438073,84.9064708 471.702073,83.9734708 C470.907104,85.7234708 472.329073,85.1394708 472.329073,85.1394708 C472.329073,85.1394708 471.827073,86.6204708 472.662073,86.6204708 C473.001073,86.6204708 473.995073,85.1394708 473.995073,85.1394708 C473.995073,85.1394708 473.995073,87.9734708 475.238073,84.4414708 C476.071073,87.7754708 477.917073,79.0904708 478.528073,77.9324708 C479.140073,76.7754708 481.459073,75.7964708 483.265073,74.8634708 C485.072073,73.9324708 486.663073,71.7724708 487.632073,69.7204708 C489.035073,66.8614708 489.662073,64.4804708 489.662073,61.3064708 C489.662073,54.2234708 492.971073,53.9734708 492.971073,53.9734708 C492.971073,53.9734708 490.976073,53.8764708 490.329073,53.2234708" id="Fill-87" fill="var(--hr-color)"></path>
<path d="M177.695104,46.218 C179.252104,47.12 180.579104,51.973 180.214104,56.075 C179.708104,61.772 180.593104,64.226 180.581104,64.709 C180.567104,64.962 180.224104,63.94 180.126104,64.119 C180.028104,64.3 179.377104,66.022 179.377104,66.022 C178.314104,65.897 177.788104,64.477 177.788104,64.477 C177.788104,64.477 177.614104,65.198 177.439104,65.022 C176.716104,64.271 177.416104,61.669 177.165104,60.689 C176.433104,57.829 174.939104,58.128 173.502104,55.834 C171.625104,52.839 171.708104,49.822 172.127104,48.584 C172.433104,47.644 172.922104,46.91 172.226104,45.906 C171.746104,45.206 171.072104,45.652 170.403104,45.709 C170.714104,45.484 171.124104,45.445 171.434104,45.219 C171.788104,44.971 175.734104,39.39 177.695104,46.218" id="Fill-90" fill="var(--hr-color)"></path>
<path d="M422.710104,55.938 C422.537104,55.574 421.710104,50.668 417.874104,51.317 C415.392104,51.738 413.710104,53.668 414.627104,57.084 C414.271104,59.936 412.264104,61.525 413.761104,67.05 C415.427104,73.204 419.112104,76.736 419.257104,78.691 C419.356104,80.243 419.127104,86.627 419.127104,86.627 C419.236104,86.528 420.370104,85.727 420.478104,85.628 C420.696104,85.909 420.845104,86.237 421.127104,86.501 C421.153104,86.355 421.604104,86.239 421.632104,86.093 C422.013104,86.465 422.516104,87.242 423.070104,87.497 C423.089104,87.079 423.173104,86.461 423.354104,86.134 C423.363104,86.406 424.202104,87.052 424.257104,87.243 C424.202104,87.052 424.678104,86.266 424.824104,85.33 C425.142104,85.72 426.640104,85.572 426.877104,85.918 C424.794104,82.584 424.141104,77.345 424.120104,75.908 C424.019104,72.43 426.519104,69.102 426.492104,65.673 C426.454104,60.903 422.710104,55.938 422.710104,55.938" id="Fill-98" fill="var(--hr-color)"></path>
<path d="M302.845104,52.885 C302.750104,52.573 302.523104,47.483 298.347104,48.334 C296.386104,48.734 295.575104,51.737 296.049104,53.353 C295.437104,55.595 294.345104,56.846 294.877104,61.459 C295.470104,66.599 298.846104,68.692 298.732104,70.277 C298.630104,71.533 298.642104,73.546 298.598104,74.85 L298.535104,75.183 C298.632104,75.116 298.744104,75.156 298.841104,75.089 C298.980104,75.342 299.019104,76.129 299.210104,76.376 C299.248104,76.262 299.903104,75.988 299.529104,75.487 C299.785104,75.832 300.989104,77.493 301.170104,77.254 C301.146104,77.474 301.582104,76.552 301.603104,76.712 C301.582104,76.552 301.681104,76.558 301.906104,75.826 C302.110104,76.178 302.440104,76.195 302.585104,76.501 C303.798104,75.019 302.642104,71.989 302.793104,70.834 C303.120104,68.033 304.624104,64.088 305.002104,61.334 C305.529104,57.506 302.845104,52.885 302.845104,52.885" id="Fill-99" fill="var(--hr-color)"></path>
<path d="M542.099104,60.139 C541.980104,59.748 542.402104,54.385 537.377104,54.3367902 C534.210104,54.307 533.793104,57.501 533.210104,60.084 C532.422104,62.891 530.212104,63.731 530.877104,69.501 C531.616104,75.928 535.338104,80.463 535.187104,82.446 C535.052104,84.019 534.894104,88.962 535.018104,88.878 C535.195104,89.194 535.379104,89.576 535.622104,89.884 C535.671104,89.741 535.845104,89.516 535.894104,89.374 C536.220104,89.805 537.677104,91.884 537.908104,91.584 C537.877104,91.859 538.835104,90.146 539.124104,89.228 C539.384104,89.669 539.485104,89.668 539.670104,90.051 C541.222104,88.192 540.271104,82.86 540.467104,81.415 C540.893104,77.911 543.928104,73.925 544.419104,70.48 C545.104104,65.688 542.099104,60.139 542.099104,60.139" id="Fill-100" fill="var(--hr-color)"></path>
</g>
</g>
</g>
</svg></p>
<p>If you’re a frequent reader of this blog, then you’ve most likely already seen what a horizontal rule looks like on here. In fact, if you haven’t seen it before, then you might have <em>just</em> done that. The birds-on-a-wire illustration above this very paragraph is a styled <code><hr></code> element.</p>
<div class="reader-mode-only">
<p class="reader-mode-only"><em>If you're reading this article in a reading app or a Web browser's reader mode, then the above horizontal rule will not appear with my custom styles, and you'll just see a horizontal line. Here is a picture of what the horizontal rule looks like:</em></p>
<figure><img src="https://sarasoueidan.com/assets/images/hr.png" alt="The horizontal rule style I am using on my Web site. It is a vector illustration of a few bird silhouettes standing on a curvey horizontal black wire." /></figure>
</div>
<p>Prior to creating this horizontal rule style, my horizontal rules looked as boring as you could possibly imagine. Until one day, I looked at that boring line and imagined a bunch of birds sitting on it <small><em>(because, well, BIRDS!)</em></small>. And then it clicked! I got an SVG image of a bunch of birds (silhouettes) on a wire, modified it to look like I wanted it to, and used it to style my <code><hr></code> elements to look like what you can see above.</p>
<p>In this post, I’ll go over how I did it, and how my horizontal rules can possibly be improved further, so that they adapt to various contexts, while remaining semantic and accessible.</p>
<h2 id="semantics-and-accessibility" tabindex="-1">Semantics and accessibility</h2>
<p>An HTML horizontal rule is, <strong>by definition, not just a visual divider.</strong> It has semantics and plays a meaningful role in the context of its surrounding content:</p>
<blockquote>
<p>The HTML <code><hr></code> element represents a paragraph-level thematic break, e.g. a scene change in a story, or a transition to another topic within a section of a reference book.</p>
</blockquote>
<p>The <code>hr</code> element has <a href="https://www.w3.org/TR/html-aria/#dfn-implicit-aria-semantics">an implicit role</a> of <code>separator</code>. As such, <code><hr></code> is understood and announced by screen readers. A <code>hr</code> is announced as “Horizontal Splitter” by VoiceOver on macOS. The <code>hr</code> is also displayed as a horizontal line by reading apps and reader modes, where your CSS is typically stripped out and HTML semantics are used to determine how an element is styled by the reading app.</p>
<p>What’s interesting is that a horizontal rule has an implicit orientation which is <code>horizontal</code> by default. This means that if you’re using <em>vertical</em> rules to split content, you can inform screen readers that the rule is vertical by setting the <code>aria-orientation</code> attribute value to <code>vertical</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>hr</span> <span class="token attr-name">aria-orientation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>vertical<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!-- Note that you needn’t & shouldn’t explicitly set `role="separator"` on the `hr` element because its semantics are implicit --></span></span></code></pre>
<p>VoiceOver on macOS announces horizontal rules with a vertical orientation as “Vertical Splitter”.</p>
<p>In addition to separating paragraphs in an article, horizontal rules can be used for separating groups of menu items in a menu, for example. They can also be interactive.</p>
<p><strong>Further reading:</strong></p>
<ul>
<li><a href="https://www.w3.org/TR/wai-aria-1.1/#separator">ARIA spec for <code>separator</code></a></li>
</ul>
<p>Since horizontal rules are semantic elements announced by screen readers, then <strong>unless you’re using a horizontal rule to <em>semantically</em> divide content</strong>, you should hide it from screen readers using <code>aria-hidden="true"</code>. In other words: use <code>aria-hidden="true"</code> to hide decorational horizontal lines. Do <em>not</em> hide them if they are providing semantic, thematic content breaks.</p>
<h2 id="styling-with-css" tabindex="-1">Styling with CSS</h2>
<p>Like other empty HTML elements, the <code><hr></code> element can be limitedly styled using CSS. For example, you can change its width and height, border, and background color, as well as use gradients or apply a background image, etc. And even though it’s a content-less element, it seems that you can even create <a href="https://sarasoueidan.com/blog/horizontal-rules/(https://css-tricks.com/examples/hrs/)">simple <code>hr</code> styles</a> using <code>::before</code> and <code>::after</code> pseudo-elements, too.</p>
<p>To create the birds-on-a-wire horizontal rules used on this site, all I did was set the dimensions of the <code>hr</code>, and apply the SVG image as a background image in CSS:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">hr</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> ..<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> ..<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"data:image/svg+xml,..."</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* .. */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>There’s nothing too fancy going on in the CSS. But the above styles have irked me ever since I wrote them. Generally, I try to avoid using background images for anything that’s not purely decoration. And the horizontal rules, as mentioned earlier, are <em>not</em> pure decoration. I also prefer to avoid using SVGs as background images and tend to inline them whenever I can so that I can take full advantage of their stylability with CSS.</p>
<p><strong>An SVG implemented as a background image cannot be styled from its containing (or referencing) stylesheet.</strong> I want the <code>hr</code> to be adaptive. I want to be able to customize it for different themes, and have the ability to modify and control its color(s) via CSS whenever I want. I also want to style it so that it remains accessible in user-controlled environments <small>(such as Windows High Contrast Mode, for example)</small>. And while I don’t plan on animating the horizontal rules on this blog, I’d still like to have the option to add a subtle animation should I ever want to. After all, SVGs are handy because they are so powerful and flexible—they’re about more than crisp lines and illustrations.</p>
<p>To style the horizontal rule to match various themes, I could possibly change the background image on the <code>hr</code> for each theme. This would work. But if the images are inlined in the CSS, then I’ll end with a larger CSS file. And if they’re not inlined, I’ll end up with multiple requests to multiple images, which isn’t great for performance. Not to mention having to create and maintain multiple images, which is not the best workflow for something that could be simpler.</p>
<p>Additionally, in Windows High Contrast Mode, we have no control over the active theme colors. So if I want to make sure the horizontal rule always has enough contrast with the background, then I’ll need to be able to change and control its colors in my CSS, which I can’t do if if the SVG is a background image. This means that I might end with a black horizontal rule on a black background, for example.</p>
<p><small><em>(On the other hand, if you don’t really care about the horizontal rule’s styles in WHCM and you just want it to remain properly accessible, you could just strip away the background image and let WHCM style the <code>hr</code> the way it would do by default.)</em></small></p>
<p>I could just use a background image on the <code><hr></code> and stop at that point. But if if I want the horizontal rule to be truly adaptive, then I need a little more flexibility that the <code><hr></code> element itself doesn’t provide…</p>
<h2 id="a-more-flexible-and-adaptive-implementation-with-inline-svg-and-aria" tabindex="-1">A more flexible and adaptive implementation with inline SVG and ARIA</h2>
<p>The best way to get the full flexibility of an SVG is by inlining it. But the <code><hr></code> element is content-less — it has no opening and closing tags within which you can place other elements.</p>
<p>The only way to work around the limitations of <code><hr></code> while preserving semantics for screen reader users is to use a <code>div</code> and provide the semantics of an <code>hr</code> using ARIA. Yes, I am cringing at the idea as I type this. But hear me out. I think it’s worth exploring and going down this road if you want to get creative with your horizontal rules while also making sure they remain visually accessible and customizable in various contexts and environments.</p>
<p>The alternative approach to creating flexible horizontal rules would essentially look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>separator<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- hide the SVG from screen readers to avoid them announcing it --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Or, as my friend <a href="https://scottohara.me/">Scott</a> pointed out, the <code>separator</code> role could be directly applied to the <code>svg</code>, in which case there’s no need to hide the SVG with <code>aria-hidden</code> anymore:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>separator<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>794px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>51px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 794 51<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using the above markup:</p>
<ul>
<li>we create a semantic horizontal rule that is understood and properly announced by screen readers, and</li>
<li>the illustration, being an inline SVG now, becomes easier to style and control with CSS.</li>
</ul>
<p>For convenience, the above horizontal rule markup could be wrapped in a custom component, or separated into a template partial or a shortcode, so that it can be more efficiently inserted where a <code><hr></code> would otherwise go.</p>
<p>With the SVG inlined, I can use CSS custom properties to control what the SVG looks like in different contexts. For example, with <a href="https://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/">CSS custom properties sprinkled inside the SVG</a>, I could then control that content using CSS, modifying and changing its colors. For example, in Windows High Contrast Mode, I could set the color of the SVG to whatever is the current text color:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* IE and Legacy Edge */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">-ms-high-contrast</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">div[role="separator"] svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">--hr-color</span><span class="token punctuation">:</span> windowText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token comment">/* Using the new standards for forced colors, currently supported in Chromium Edge */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">div[role="separator"] svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">--hr-color</span><span class="token punctuation">:</span> CanvasText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>That way, the horizontal rule will always have enough contrast with the background behind it.</p>
<p>If the content of the SVG is multi-colored, you can define and set values for multiple variables in the same manner, thus creating different versions—or themes—of the horizontal rule for different contexts or environments.</p>
<p><strong>Further reading:</strong></p>
<ul>
<li><a href="https://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/">Styling SVG <code><use></code> Content with CSS</a>, in which I elaborate more about theming SVG images using CSS custom properties</li>
<li><a href="https://blogs.windows.com/msedgedev/2020/09/17/styling-for-windows-high-contrast-with-new-standards-for-forced-colors/">Styling for Windows high contrast with new standards for forced colors</a> <small>(Hat tip: <a href="https://twitter.com/aardrian/status/1375201979849408514?s=20">Adrian Roselli</a>)</small></li>
</ul>
<p>Using this technique:</p>
<ul>
<li>The horizontal rule is styleable and customizable using CSS.</li>
<li>It can be animated with CSS and/or JavaScript.</li>
<li>It will display like it is intended to (birds on a wire) even in environments where my CSS is stripped off, such as reading apps. Because the it is now an <em>image</em>, it will be displayed just the way it is, as opposed to an <code><hr></code> that would be displayed and styled by the reading app.</li>
<li>Using the high contrast media query, it can be adapted to always remain accessible with whatever user theme is currently active.</li>
</ul>
<p>Here is a video of me changing the color of my birds-on-a-wire horizontal rule in realtime using Chrome devtools. This is made possible by using inline SVG and CSS custom properties to apply the color to the SVG content:</p>
<figure>
<div class="video">
<iframe src="https://player.vimeo.com/video/528984771?badge=0&autopause=0&player_id=0&app_id=58479" width="100%" height="auto" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" title="theming with inline SVG and CSS custom properties"></iframe>
</div>
</figure>
<h2 id="closing-thoughts" tabindex="-1">Closing Thoughts</h2>
<p>Just like using inline SVG for icons is the best practice to ensure the icons remain adaptive and accessible, SVG can similarly provide better resilience for horizontal rules when you want to get more creative with them. Whether or not you use this technique, it’s good to have in your knowledge base, just in case you ever need it.</p>
Accessible Text Labels For All
2021-03-17T00:00:00Z
https://sarasoueidan.com/blog/accessible-text-labels/
<p>My talk <em><strong>Applied Accessibility: Practical Tips for Creating more Accessible Front-Ends</strong></em> is now <a href="https://youtu.be/Mv_RlmAm4nc">available to watch online</a>. This blog post is an extended transcript for a section of the talk, in which I discuss how to create more descriptive button (and/or link) text labels that improve the e-commerce experience for screen reader users, whilst making sure those buttons don’t fail WCAG success criteria. This, in turn, ensures that the buttons remain usable by another category of users: those using voice commands to navigate the Web. The concepts covered are applicable to all kinds of text labels, including form control labels.</p>
<hr />
<h2 id="quick-overview-of-the-voiceover-rotor-on-macos-and-how-it-can-be-used-to-display-contents-on-a-page" tabindex="-1">Quick overview of the VoiceOver Rotor on macOS and how it can be used to display contents on a page</h2>
<p><a href="https://www.apple.com/voiceover/info/guide/_1121.html">VoiceOver</a> (VO) is the built-in screen reader on macOS. VoiceOver users can navigate the Web using what is known as the Web Rotor.</p>
<p>The VoiceOver Web Rotor is a quick way to explore and navigate a web page. It is like power commands but for screen reader users. It increases their efficiency and facilitates their browsing the web.</p>
<p>With VoiceOver open, the rotor can be activated with the keyboard shortcut <kbd>ctrl</kbd> + <kbd>option</kbd> + <kbd>U</kbd>. Once open, the rotor will be visible as a heads up display in the middle of the screen. It then presents all items of a particular type in a list.</p>
<p>Inside the rotor, there are several lists—or menus— that the user can use to explore and navigate the content on the page. Using the rotor is an easy way to hear, for example, all the links on a page. The user can then jump to any link they want. There is also a headings menu that allows the user to jump to a particular heading on the page.</p>
<figure class="wide">
<div class="video">
<iframe src="https://player.vimeo.com/video/524890417?badge=0&autopause=0&player_id=0&app_id=58479" width="100%" height="auto" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" title="VoiceOver Demo 2 with Audio"></iframe>
</div>
<figcaption>In this video, I am demoing the VoiceOver Web Rotor to navigate and explore sections on an A List Apart page. Pressing the right and left arrow inside the rotor shows the different menus available, and gives a quick overview of the different types of content on the page.</figcaption>
</figure>
<p>Using the rotor, and if the document is using semantic markup and proper sectioning elements (such as <code>nav</code>, <code>header</code>, <code>main</code>, <code>footer</code>, <code>aside</code>, and so on), the user can move from one area of the page to another without having to go through the content in each section. And it is <em>very</em> important for them to be able to do so.</p>
<p>Note that <strong>the document structure in the rotor is defined by the document structure in your HTML</strong>. So, if you don’t provide a heading, it won’t show up in the rotor and so the document hierarchy for screen reader users will be affected. If you don’t use a landmark, it won’t show up in the rotor and the user won’t be able to jump to it to use it.</p>
<h3 class="h5">Useful Reading: </h3>
<ul>
<li><a href="https://kb.iu.edu/d/atgb">Voiceover Basics</a></li>
<li><a href="https://etc.usf.edu/techease/4all/vision/how-do-i-use-webrotor-in-voiceover/">Using the Web Rotor to Navigate a Web Page with VoiceOver</a></li>
</ul>
<hr />
<p>We mentioned earlier that the rotor contains different menus, including Headings menu, Landmarks menu, a Links menu, and a Form Controls menu. Knowing how a screen reader user might navigate a page opens up the possibility to optimize our interfaces and improve their user experience even more.</p>
<h2 id="exploring-e-commerce-product-listings-using-the-voiceover-web-rotor" tabindex="-1">Exploring e-commerce product listings using the VoiceOver Web rotor</h2>
<p>A common design in e-commerce Web sites is displaying a list of products on a page, each with its own <em><strong>Add to Cart</strong></em> button, allowing the user to quickly add items to their cart.</p>
<p>To demonstrate, here is the <a href="https://www.yeti.com/en_US/bottles">Yeti Web site</a>, showcasing a list of reusable bottles. Each bottle comes with its own <em>Add to Cart</em> button. Some also have a Customization option. The following video is a demo of me exploring the page using the VoiceOver Web Rotor:</p>
<figure class="wide">
<div class="video">
<iframe src="https://player.vimeo.com/video/524924279?badge=0&autopause=0&player_id=0&app_id=58479" width="400" height="300" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" title="VO Rotor Form Controls Menu demo"></iframe>
</div>
<figcaption></figcaption>
</figure>
<p>When you navigate this page using VoiceOver, and use the Form Controls menu, you’ll get a list of all form controls on the page, including the <em>Add to Cart</em> buttons.</p>
<p>Quickly scanning these buttons you can tell that they provide very little value, as there is no way to tell which product each button corresponds to. How does a user know which button they want to go to and press if they don’t know which product it corresponds to?</p>
<p>You may have already guessed that one way we could improve the navigation is by adding screen <a href="https://sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/#hiding-content-in-css-and-html">visually-hidden, reader-only text</a> that would add the name of the product to its corresponding button.</p>
<p>So, technically, the solution <em>could</em> look something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><button type=".." class=“.."></span><br /><span class="highlight-line"> Add <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>PRODUCT_NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> to Cart</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!-- P.S. Don’t do this --></span></span></code></pre>
<p>You include the product name in the button’s text label string, so then when the Form Controls menu displays the list of buttons, each button would be clearly labelled to indicate which bottle it is referring to.</p>
<p>This is a good solution for a screen reader user navigating using Form Controls, but <strong>you do <em>not</em> want to do this</strong> because <strong>even though it improves the experience of some screen reader users, it excludes users of other assistive technologies</strong> and makes these buttons inaccessible to them, and a pain to use…</p>
<h2 id="talking-your-way-through-web-browsing-with-voice-commands" tabindex="-1">Talking your way through Web browsing with voice commands</h2>
<p>Inserting visually hidden text in the middle of a visible string of text on a button like that prevents users browsing and navigating using voice commands from interacting with the button.</p>
<p>A popular example of Voice recognition software used to browse the Web is <a href="https://www.nuance.com/dragon/business-solutions/dragon-professional-individual.html">Dragon Naturally Speaking</a>. It is software that allows you to use your computer and browse the web using voice commands. It comes with a full usage manual that details how to use it to perform different tasks on your computer and on the Web. Such software is useful for a lot of people, including but not limited to people with disabilities who can’t use their hands, for example, or power users who want to get things done faster (because voice dictation is faster than typing).</p>
<p>Seeing it in action is the best way to get an idea of how it’s used.
So, to quickly demonstrate how it is used on the Web, <a href="https://www.levelaccess.com/">Level Access</a> created <a href="https://www.youtube.com/watch?v=kJKQmTumFP0&ab_channel=LevelAccess">a video demoing Dragon Naturally Speaking to fill out a form on a page</a>. The following video is a short clip from their video which you can find and <a href="https://www.youtube.com/watch?v=kJKQmTumFP0&ab_channel=LevelAccess">watch in full on Youtube</a>.</p>
<p><small>Note: Note that when he says “go to sleep” or “wake up” he’s basically pausing Dragon and reactivating it by telling it to sleep and wake up.</small></p>
<figure class="wide">
<div class="video">
<iframe src="https://player.vimeo.com/video/524959505?badge=0&autopause=0&player_id=0&app_id=58479" width="936" height="540" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" title="Dragon Naturally Speaking demo excerpt"></iframe>
</div>
<figcaption></figcaption>
</figure>
<p>When the dragon user (in the video) wants to select a form control, <strong>he speaks out the visual text label of that control</strong>. This is one of many reasons why <strong>visual labels are important in user interfaces</strong>.</p>
<p>So when we have a series of <strong><em>Add to Cart</em></strong> buttons, a dragon user <strong>will speak the label of the button in order to interact with it</strong>. This is why adding text in the middle of the string makes it inaccessible. The content in the middle of the string would break the visible label. The user would be telling dragon to interact with a button whose label is not what it visually appears to be. This is why this would fail the <a href="https://www.w3.org/WAI/WCAG21/Understanding/label-in-name.html">WCAG Success Criterion 2.5.3: Label in Name</a>: <q>For user interface components with labels that include text or images of text, the name contains the text that is presented visually. A best practice is to have the text of the label at the start of the name.</q></p>
<p>The following two paragraphs are from the Success Criterion’s page (emphasis mine):</p>
<blockquote>
<p>The intent of this Success Criterion is to ensure that the words which visually label a component are also the words associated with the component programmatically. This helps ensure that people with disabilities can rely on visible labels as a means to interact with the components.</p>
<p></p><p>Most controls are accompanied by a visible text label. Those same controls have a programmatic name, also known as the Accessible Name. <strong>Users typically have a much better experience if the words and characters in the visible label of a control match or are contained within the accessible name.</strong> When these match, speech-input users (i.e., users of speech recognition applications) can navigate by speaking the visible text labels of components, such as menus, links, and buttons, that appear on the screen. <strong>Sighted users who use text-to-speech (e.g., screen readers) will also have a better experience if the text they hear matches the text they see on the screen.</strong></p>
<cite><a href="https://www.w3.org/WAI/WCAG21/Understanding/label-in-name.html">WCAG Success Criterion 2.5.3</a></cite><p></p>
</blockquote>
<h2 id="improving-the-experience-for-screen-reader-users-whilst-keeping-it-accessible-to-speech-input-users" tabindex="-1">Improving the experience for screen reader users whilst keeping it accessible to speech-input users</h2>
<p>We still want to improve the experience for screen reader users, but we can’t insert the product name into the button’s visible label. We can’t fix the experience for a group of people and end up breaking it for another group. Challenges like these make you scratch your head and think of alternative solutions.</p>
<p>It turns out, there is a middle ground here. We <em>can</em> still add the product name to the button, improving the user experience for screen reader users, <em>without</em> breaking the visible name of the button, <strong>by <em>appending</em> it to the end of the button’s name, instead of inserting it in the middle.</strong></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><button type=".." class=“.."></span><br /><span class="highlight-line"> Add to Cart <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>, PRODUCT_NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>By appending the text to the end of the visible name, the visible name is left intact, and the Web Rotor Form Controls menu will show a list of <em><strong>Add to cart</strong></em> buttons with the names of the products they are referring to appended to them.</p>
<p>As for the voice control user, when they say “<em>click Add to Cart</em>”, Dragon is going to label the <em>Add to Cart</em> buttons with numbers, like we saw with the checkbox and radio button examples in the video, and then the user can speak out the number of the button they want to click. This works because Voice commands work by saying the name of the input you want to interact with, as long as the name is not “broken” or interrupted by content in the markup.</p>
<p>So whenever you need to add additional text to a visible label, it best come after what’s visually shown.</p>
<p>Similarly, always ensure the accessible name (announced by screen readers) matches the visual label as much as possible. This means that you’ll also want to avoid adding a label using <code>aria-label</code> that does not match the text label that is shown on a control.</p>
<h2 id="closing-thoughts%3A-provide-visual-labels-whenever-possible" tabindex="-1">Closing thoughts: Provide visual labels whenever possible</h2>
<p>Knowing how voice control users navigate the Web can also inspire more visual improvements to our components.</p>
<p>If the user needs to see a visible label to interact with a control, then what about controls that don’t have any visible labels? What about components that have label-less controls, such as dot navigations (typically used in sliders and carousels)? If a user wants to interact with the dots in a dot navigation, they can’t speak their accessible name because they don’t have a visible one they can see. So they are left with two other ways to navigate: using voice commands to tab through the control, or using the mouse grid, which is the most tedious and least favorable option.</p>
<p>It should be obvious by now that the best practice is to always have a visible label for UI controls. But if that’s not possible at all, then the next possible option is to <em>show</em> a label on focus (and hover, too, maybe). Showing labels <em>on focus</em> is particularly important because a Dragon user can tell Dragon to <strong>Tab</strong> to the next control, but they can’t tell it to hover over it (what’s it going to hover if it has no label to indicate what it is?)</p>
<p>Other visual elements that would be more accessible when associated with a visible label are <a href="https://www.sarasoueidan.com/blog/accessible-icon-buttons/">icon buttons</a>. Similar to the dot navigation buttons, if you can’t add a visible label, try showing one when the user interacts with the button.</p>
<p>Thank you for reading.</p>
How I set up Glyphhanger on macOS for optimizing and converting font files for the Web
2021-03-02T00:00:00Z
https://sarasoueidan.com/blog/glyphhanger/
<p class="size-2x">
I can't count the number of times I've tried installing a command line tool on my machine, only to find myself going down a black hole of node modules & dependencies, and a seemingly non-ending list of error messages in the terminal. This would go on for a while before I finally give up and call it quits, only to revert back to find myself googling the name of an online app and using that to do what I need instead.
</p>
<p>Yesterday, the same thing <em>almost</em> happened again. I got into another black hole in iTerm as I tried to install a couple of Web font conversion and optimization tools. I almost gave up before I finally managed to <em>kind of</em> get everything working the way I wanted it to. A lot of googling and “stack-overflowing” was involved in this process. I’m sharing my struggles in this post in the hopes that it might help someone out should they get stuck where I did. I also want to have a reference to come back in the future should I ever find myself installing the same tools again.</p>
<hr />
<p>Yesterday was just another typical work day. I was setting up the foundation for a new client project. The Web site we’re building uses the Inter font family for the typography. So, I head over to <a href="https://fonts.google.com/">Google Fonts</a>, found <a href="https://fonts.google.com/specimen/Inter?preview.text_type=custom">the Inter family page</a>, and clicked that <strong>“Download family”</strong> button.</p>
<p>As you probably already know, Google Fonts doesn’t supply the fonts in WOFF or WOFF2 formats. When you download a font, you only get TTF font files. Why that is, I don’t know. Jeremy <a href="https://adactio.com/journal/17480">has wondered the same</a> before. And if you’re a front-end developer you should also know that TTF is not the best choice for serving Web fonts, and that WOFF and WOFF2 are far leaner and more performant.</p>
<p>So, as usual, I set out to convert the TTF files to WOFF and WOFF2 to serve them up in my project.</p>
<h2 id="my-previous-font-conversion-workflow" tabindex="-1">My previous font conversion workflow</h2>
<p>Normally, I’d head over to one of the online tools available — whatever comes up first in Google search, because I’ve never bookmarked one tool to use at all times, even though I tend to use <a href="https://www.fontsquirrel.com/tools/webfont-generator">Font Squirrel generator</a> quite often. But that has always felt like too much of a hassle. Ideally, I’d be able make this conversion <em>within the folder in which they are located</em>.</p>
<p>So the workflow would look like this:</p>
<ol>
<li>download font,</li>
<li>unzip & open folder,</li>
<li>convert to WOFF & WOFF2,</li>
<li>copy to project folder and embed on page.</li>
</ol>
<p>…<strong>instead of:</strong> download font, unzip & open folder, find an online tool, upload font files, wait for conversion, download fonts, unzip & open folder, and then move to project folder and embed on the page. This has always felt like an clunky workflow. It always bothered me that I had to click and switch contexts as much as I did.</p>
<hr />
<p>So, yesterday <a href="https://twitter.com/SaraSoueidan/status/1366275965215793153?s=20">I saught the help of the amazing Twitter #lazyWeb</a> and asked my friends there what they use to convert their font files on macOS. Many responded with links to online tools, which is what I’m wanting to avoid. And some responded with recommendations for a few command line tools.</p>
<p>I imagined I’d want a nice drag and drop tool for this. But command line tools are also pretty handy, and usually very flexible, too. Once you’ve got one set up, it could help streamline your font conversion process. And since I’m much more comfortable in the command line today than I was a few years ago, I thought it was about time I switched to one of those tools.</p>
<p>I was instantly sold on <a href="https://www.filamentgroup.com/">Filament Group</a>’s <a href="https://github.com/filamentgroup/glyphhanger"><code>glyphhanger</code></a>. “<code>glyphhanger</code> is a useful tool when working with web fonts—it can help optimize your font files very quickly”.</p>
<p>Not only does <code>glyphhanger</code> convert font files, but it also subsets them <small>(i.e. it removes unnecessary characters from a font file)</small>, which is another optimization step you want to make to serve more performant fonts. (That, and the fact that I love the work of the folks at Filament Group, who have always been big proponents of performance.)</p>
<p>The <a href="https://github.com/filamentgroup/glyphhanger">Github repository</a>’s README file includes installation instructions. And that’s where I started yesterday…</p>
<h2 id="installing-glyphhanger" tabindex="-1">Installing Glyphhanger</h2>
<p>I’m going to start with the process I went through and the issues and errors I got. If you’re not interested in that, you can skip straight to <a href="https://sarasoueidan.com/blog/glyphhanger/#setup">the solution</a>.</p>
<h3 id="down-the-rabbit-hole" tabindex="-1">Down the rabbit hole</h3>
<p><code>glyphhanger</code> is available on npm. According to the reposiroty’s README, installation is as simple as running:</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line"><span class="token function">npm</span> <span class="token function">install</span> -g glyphhanger</span></code></pre>
<p>I ran that command. <code>glyphhanger</code> was installed. Yay. So far so good.</p>
<p>Following that line in the README is a note that says that <code>pyftsubset</code> is a prerequisite. I wasn’t familiar with what <code>pyftsubset</code> is, so I kept on reading. <small>(Honestly, does everyone know what everything is?)</small></p>
<p>There was a link to another Github repository: <a href="https://github.com/fonttools/fonttools">fontTools</a>. I chose <code>glyphhanger</code> for its subsetting feature, and the subsetting functionality requires you to install <a href="https://github.com/fonttools/fonttools">fonttools</a>. So, I had a dependency to install. Reading the README of the fontTools repo only complicated things for me. So, I kept on reading the <code>glyphhanger</code> instructions.</p>
<p><small>(At this point: I couldn’t make the direct connection between <code>pyftsubset</code> and fontTools at first. Later on, I googled “pyftsubset fonttools” and learned that <code>pyftsubset</code> is an OpenType font subsetter and optimizer, based on fontTools. And that’s why you need to install fontTools.)</small></p>
<p>The next instruction is:</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">pip <span class="token function">install</span> fonttools</span></code></pre>
<p>I had no idea what <code>pip</code> is. I decided to run the command before googling anything. Unsurprisingly, that’s when I got my first error.</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">zsh: <span class="token builtin class-name">command</span> not found: pip</span></code></pre>
<p>Oups. OK so apparently I need to have Pip installed on my machine. Not knowing what it is, I started googling. I learned that Pip is a package manager for Python. And that to install Pip, I needed to have Python installed.</p>
<p>Many articles mentioned that macOS already comes with a version of Python installed. So I looked for what I needed to install Pip using the Python that’s already installed. For some reason, none of the commands I found worked. So I thought: “maybe Python <em>isn’t</em> installed on my machine?”. So I opened a new browser tab and started googling for how to install Python.</p>
<p>¯_(ツ)_/¯</p>
<p>While I was at it, I was following the rest of the instructions in the README. I didn’t know what depended on what, so I thought I’d try continuing async.</p>
<p>The following instructions are about cloning the Brotli and Zopfli git repositories and installing them because they are required to get WOFF and WOFF2 support:</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line"><span class="token comment"># Additional installation for --flavor=woff2</span></span><br /><span class="highlight-line"><span class="token function">git</span> clone https://github.com/google/brotli</span><br /><span class="highlight-line"><span class="token builtin class-name">cd</span> brotli</span><br /><span class="highlight-line">python setup.py <span class="token function">install</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"># Additional installation for --flavor=woff --with-zopfli</span></span><br /><span class="highlight-line"><span class="token function">git</span> clone https://github.com/anthrotype/py-zopfli</span><br /><span class="highlight-line"><span class="token builtin class-name">cd</span> py-zopfli</span><br /><span class="highlight-line"><span class="token function">git</span> submodule update --init --recursive</span><br /><span class="highlight-line">python setup.py <span class="token function">install</span></span></code></pre>
<p>As you might have noticed already, these steps require Python to work, and since I didn’t have Python working for me at this point yet, I got quite a bunch of errors in the terminal. Trying to install Brotli and Zopfli like that confirmed to me that Python maybe is, indeed, <em>not</em> installed by default.</p>
<p>fontTools is also a Python project, which, naturally, requires Python to work. (Duh.)</p>
<p>So it was very obvious at this point that what I needed to focus on was getting Python and Pip set up if I wanted any of this to work.</p>
<p>After googling quite a bit and jumping between articles and Stack Overflow Q&As, I learned that there are multiple versions of Python (obviously), the latest (I think) is Python 3. Now this is where the weirdness started. After installing Python 3, if I wanted to run a Python command, I needed to use <code>python3</code> followed by the command. Not <code>python</code>. According to <a href="https://www.addictivetips.com/mac-os/install-python-3-on-macos/">a nice article I found later</a>, “in order to send commands to Python 3, you will need to enter <code>python3</code> in the terminal. If you enter <code>python</code>, the command will be sent to Python 2.”</p>
<p>So anyway, I installed Python using <code>brew</code>:</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">brew <span class="token function">install</span> python3</span></code></pre>
<p>Yep. That worked. I don’t remember if I had any issues here. But then again, I had so many issues that I don’t remember when and where I got each.</p>
<p>¯_(ツ)_/¯</p>
<p><small><strong>Here’s a quick tip:</strong> If you, like me, want to avoid <code>brew</code> updating every time you use the <code>brew</code> command, you can use <code>HOMEBREW_NO_AUTO_UPDATE=1</code>.
I have a snippet setup so that every time I type <code>brew</code> and press TAB, it autocompletes to <code>HOMEBREW_NO_AUTO_UPDATE=1 brew</code>, so that I can run whatever brew command I want without allowing it to update. I learned this a few months ago when I got sick of brew updating every time I wanted to run a quick command. I googled, and found this tip on Stack Overflow and have been using it since.</small></p>
<p>Next, I needed to install Pip. Once again, I found several articles, each mentioning a different way to do it. To be quite honest, I don’t remember which one of those I ended up following. I did learn though that with Python 3 comes Pip 3.</p>
<p>I installed Pip 3.</p>
<p>At this point, I realized I should have everything I needed to continue going through the README instructions. So, again, I tried installing <code>fonttools</code> using <code>pip install fonttools</code>. After getting yet more errors, I learned that, just like <code>python3</code> I need to use <code>pip3</code> instead of <code>pip</code>.</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">pip3 <span class="token function">install</span> fonttools</span></code></pre>
<p>That worked. So I got Python, Pip, and fontTools out of the way.</p>
<p>To generate optimized WOFF and WOFF2 font files, I needed Brotli and Zopfli. I have no idea why I still got some errors by following the installation instructions in the README (cloning the git repos for each). So, more googling ensued. Turns out, installing Brotli and Zopfli was much easier now that I had Pip installed. I didn’t even need to clone the git repositories like the README instructed. All I needed is these two commands:</p>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">pip3 <span class="token function">install</span> brotli</span><br /><span class="highlight-line">pip3 <span class="token function">install</span> zopfli</span></code></pre>
<p>And <em>Boom!</em> all dependencies were installed. <em>Now</em> when I used <code>glyphhanger</code> to convert my font files, it finally generated the optimized TTF, WOFF, and WOFF2 files.</p>
<p>So, to sum up:</p>
<h3 id="setup"> tl;dr: installing all dependencies and running glyphhanger</h3>
<ol>
<li>Install <code>glyphhanger</code>:</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line"><span class="token function">npm</span> <span class="token function">install</span> -g glyphhanger</span></code></pre>
<ol>
<li>Install Python 3:</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">brew <span class="token function">install</span> python3</span></code></pre>
<ol>
<li>
<p>Install pip. Python 3 comes with pip 3. Follow the instructions in <a href="https://www.addictivetips.com/mac-os/install-python-3-on-macos/">this article</a> to install pip.</p>
</li>
<li>
<p>Install fontTools using pip:</p>
</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">pip3 <span class="token function">install</span> fonttools</span></code></pre>
<ol>
<li>Install Brotli and Zopfli using pip:</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">pip3 <span class="token function">install</span> brotli</span><br /><span class="highlight-line">pip3 <span class="token function">install</span> zopfli</span></code></pre>
<ol>
<li>And then use <code>glyphhanger</code> to subset and create optimized formats of your TTF font files:</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="highlight-line">glyphhanger --subset<span class="token operator">=</span>*.ttf</span></code></pre>
<p>Zach Leatherman has <a href="https://www.zachleat.com/web/glyphhanger/">a post</a> that introduces <code>glyphhanger</code> with examples of how to use it. You’ll want to read his article and the repository’s README for further customizations and usage options because there are quite a few.</p>
<h2 id="optimizing-variable-fonts" tabindex="-1">Optimizing Variable Fonts</h2>
<p><code>glyphhanger</code> also generates WOFF2 files for Variable fonts. The file size of the subsetted Variable Inter WOFF2 file generated by <code>glyphhanger</code> is slightly smaller than the file size generated by Google’s <a href="https://github.com/google/woff2">woff2 library</a> (302KB compared to 308KB). It does not break the Variable axes of the font like other online tools might, so it is safe to use it in your projects.</p>
<p><strong>Useful resources:</strong></p>
<ul>
<li><a href="https://clearleft.com/posts/how-to-use-variable-fonts-in-the-real-world">How to use variable fonts in the real world</a></li>
<li><a href="https://henry.codes/writing/how-to-convert-variable-ttf-font-files-to-woff2/">How To Convert Variable TTF Font Files to WOFF2</a> — this post explains how to set up the woff2 library and use it to generate WOFF2 from a Variable TTF font file. I’ve found it to be more helpful than the library’s README file. Make sure to follow the steps and commands to the letter.</li>
</ul>
<hr />
<p>While I finally managed to get <code>glyphhanger</code> set up and can now use it generate optimized Web font files, it still bugs me that I am <em>still</em> getting an error when using it. My terminal looks like this every time I run the command:</p>
<img src="https://sarasoueidan.com/assets/images/glyphhanger-error.png" alt="Screenshot of the errors shown in the terminal after running the glyphhanger command to subset and generate font files." />
<p>I googled a lot trying to find what that error means and where it’s coming from. I would love to get a clean output every time I run <code>glyphhanger</code>. I mean, it’s such a great tool. I want the output to be just as great. But after spending <em>quite</em> some time on getting it to work, I wasn’t left with enough patience to figure the solution out. If you have an idea what might be causing it, please do <a href="https://twitter.com/SaraSoueidan">let me know</a> — I’d appreciate it a lot.</p>
<p>✨ <strong>UPDATE:</strong> ✨ Twitter came to the rescue once again. <a href="https://twitter.com/LurkGently">@GentlyLurk</a> figured out that the issue is coming from the version of <code>shelljs</code> used in <code>glyphhanger</code>, and kindly <a href="https://twitter.com/GentlyLurk/status/1366695429212172288?s=20">shared the fix</a>:</p>
<ol>
<li>Run <code>which glyphhanger</code> in the command line. This should give you the path to the folder inside which <code>glyphhanger</code> resides. (My result was <code>/usr/local/bin/glyphhanger</code>; it’s a hidden folder so I had to <code>cmd+shift+.</code> to view it.)</li>
<li>Find the <code>package.json</code> file in the folder and change the version of <code>shelljs</code> to <code>0.8.4</code>, then</li>
<li>run <code>npm install</code> (inside the folder)</li>
</ol>
<p>Now the errors are finally gone. 🥳 If I run <code>glyphhanger --subset=Inter-Light.ttf</code> for example, the result I get in the command line looks like this:</p>
<img src="https://sarasoueidan.com/assets/images/glyphhanger-no-errors.png" alt="Screenshot of the terminal after running the glyphhanger command showing only the result of generating 3 optimized font files, with no errors whatsoever." />
<p>(Look at those WOFF2 savings!)</p>
<p>🎉🎉🎉</p>
<p>✨ <strong>UPDATE #2 (March 11, 2021):</strong> ✨ <a href="https://twitter.com/zachleat/status/1370028833882132481">Zach announced</a> the release of glyphhanger v4, which includes dependency updates that fix the console warning issue that I had and that required the manually changing the <code>shelljs</code> version in the package.json file. So if you follow the installation steps above, you should have a clean console output without the extra hassle. 🎉</p>
<h2 id="more-articles-on-font-subsetting" tabindex="-1">More articles on font subsetting</h2>
<ul>
<li><a href="https://fuzzylogic.me/posts/how-i-subset-web-fonts/">How I subset web fonts</a></li>
<li><a href="https://www.zachleat.com/web/css-tricks-web-fonts/">Developing a Robust Font Loading Strategy for CSS-Tricks</a></li>
</ul>
Inclusively Hiding & Styling Checkboxes and Radio Buttons
2020-06-16T09:11:58Z
https://sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/
<p class="deck">Checkboxes and radio buttons are two common examples of interactive form elements that we desperately want to have full control over styling but we don’t. So we’ve been hacking our way around styling them by hiding said elements with CSS and <em>visually</em> replacing them with pseudo-elements or an SVG image — SVG, of course, being the more flexible, powerful, and accessible replacement. But an SVG image is, at the end of the day, just an <em>image</em>, so while it can visually replace a checkbox, it doesn’t really substitute for it — the user still needs a <em>checkbox</em> to interact with. So, when we attempt to hide the checkbox we want to style, we need to make sure that the checkbox remains accessible and interactive.</p>
<p>I’ve recently come across quite a few articles on the topic of accessibly styling checkboxes and radio buttons. All of the articles I read use one or another variation of <a href="https://a11yproject.com/posts/how-to-hide-content/">the visually-hidden utility class</a> which is usually used to hide content visually while keeping it screen reader-accessible. But while this technique works for some content, it’s not suitable for hiding interactive elements like radio buttons and checkboxes that have other <a href="https://24ways.org/2018/inclusive-considerations-when-restyling-form-controls/">accessibility and usability considerations</a>. I learned this when I used the same technique myself to create my own accessible checkboxes a couple of years ago and my friend <a href="https://scottohara.me/">Scott O’Hara</a> kindly pointed out during one of our chats that they weren’t entirely accessible because they weren’t discoverable by all screen reader users, particularly those navigating by touch.</p>
<p>So, in this article, I will cover the different techniques for hiding elements, how each of them affects the accessibility of the content, and how to properly hide checkboxes and radio buttons taking their own accessibility and usability considerations into account to make sure we aren’t leaving any users out.</p>
<p>Note that while I will be talking about checkboxes in this article, this technique applies to radio buttons and any other interactive form elements that you may want to restyle using an image replacement, including file inputs, for example.</p>
<h2 id="setting-the-foundation-in-the-markup" tabindex="-1">Setting the foundation in the markup</h2>
<p>Even though <a href="https://css-tricks.com/custom-styling-form-inputs-with-modern-css-features/">styling a checkbox using modern CSS features</a> is currently possible, using SVG to create custom checkboxes remains, in my opinion, the most flexible, powerful, and accessible way.</p>
<p>Using SVG, we don’t style the checkbox itself — <strong>we hide the checkbox and use an SVG to create a checkbox <em>image</em></strong>. So the SVG is just a visual replacement of the checkbox.</p>
<p>So, in order to style a checkbox with SVG, we need to add the SVG to the markup somewhere. You could, of course, use the SVG as a background image (on the checkbox <code>label</code>), yes; but it comes with drawbacks such as:</p>
<ul>
<li>you lose the ability to animate the SVG, which is one of the major benefits of using SVG to begin with, and</li>
<li>you lose the ability to optimize the SVG for user-controlled environments, such as Windows High Contrast Mode, so you could end up risking the accessibility of the checkbox in those environments.</li>
</ul>
<p>…not to mention that inlining an SVG has overall more advantages than any other embedding techniques and that you could make use of, such as animations.</p>
<p>I like to wrap my checkboxes inside their labels. Placing the checkbox inside the label increases the overall clickable area, which makes it more usable. I also like this approach because it makes the checkbox a more self-contained component that I can <a href="https://www.sarasoueidan.com/blog/style-settings-with-css-variables/">customize with CSS variables</a> and use anywhere I need it.</p>
<p>Since the checkbox is going to go inside the <code><label></code>, the SVG will too.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-custom-checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-checkbox<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-4 -4 39 39<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- The background --></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox__bg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-2<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-2<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>currentColor<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token attr-name">rx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span><br /> <span class="token attr-name">ry</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- The checkmark--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>polyline</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox__checkmark<span class="token punctuation">"</span></span> <span class="token attr-name">points</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>4,14 12,23 28,5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>transparent<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>4<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>polyline</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>The checkbox label text<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>The checkbox label now contains the label text, the checkbox itself, as well as the SVG image that will represent our checkbox visually. So, if you think about it, we’re technically going to style the <em>label</em> — or a part of it.</p>
<p>A couple of important things to note here:</p>
<ol>
<li>Since the SVG is going to replace the checkbox <strong>visually, it also needs to visually convey state</strong> (checked, unchecked, disabled), <strong>as well as behavior</strong> (focus in particular, and hover if you need that).</li>
<li>The SVG is used to create <em>an image</em> of a checkbox. <strong>The SVG image is not going to replace the checkbox semantically.</strong></li>
</ol>
<p>To address the first point, I placed the SVG image <em>after</em> the checkbox in the DOM. This will allow me to use the siblings selector in CSS to select the SVG and style and animate it when the checkbox is focused and interacted with (checked and unchecked). We’ll do that in a later section.</p>
<p>And since the image doesn’t replace the checkbox semantically, the checkbox needs to remain accessible. So when we hide it, we want to make sure we do so <em>accessibly</em>.</p>
<h2 id="hiding-content-in-css-and-html" tabindex="-1">Hiding Content in CSS and HTML</h2>
<p>There are several ways we can hide content in CSS and HTML, each with its own pros and cons. Knowing the upsides and downsides of each technique will help us choose the one we need when we need it.</p>
<h3 id="hiding-content-in-css" tabindex="-1">Hiding content in CSS</h3>
<p>There are four properties in CSS that can be used to hide content:</p>
<ul>
<li>Using <code>display: none;</code></li>
<li>Using <code>visibility: hidden</code></li>
<li>Using <code>opacity: 0</code></li>
<li>Using <code>clip-path: inset(100%)</code></li>
</ul>
<p>Both <code>display: none</code> and <code>visibility: hidden</code> remove the element they hide from the DOM and <a href="https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree">accessibility tree</a>, thus making them <strong>completely inaccessible</strong>.</p>
<p>Back in the days when we used to use background image sprites to style checkboxes and radio buttons, we used to use <code>display: none</code> to hide the inputs, which removed them from the accessibility tree and therefore made them completely inaccessible to screen readers. <strong>You should never hide content using <code>display: none</code> or <code>visibility: hidden</code> if you want that content to remain accessible.</strong> We <em>need</em> our inputs to remain accessible to screen readers, so we not be using <code>display: none</code> or <code>visibility: hidden</code> anymore.</p>
<p><small>It’s also worth mentioning that you shouldn’t rely on background images or background colors to replace essential content (such as inputs) because background images are not accessible to screen readers, not to mention that they are most likely going to be removed when your CSS isn’t applied in user-controlled environments (such as Windows High Contrast Mode) and reader modes.</small></p>
<h3 id="hiding-content-in-html" tabindex="-1">Hiding content in HTML</h3>
<p>We can also hide content straight from the HTML using HTML attributes. There are two attributes we can use today: <code>hidden</code> and <code>aria-hidden</code>.</p>
<dl>
<dt>The <code>hidden</code> attribute </dt>
<dd> <ul><li>is the HTML equivalent of CSS’s <code>display: none</code>,</li><li> it hides the element it is applied to both visually and from assistive technologies,</li> <li> and is useful for hiding content when CSS is disabled (for example, in reader modes).</li></ul></dd>
<dt>The <code>aria-hidden</code> attribute</dt>
<dd><ul><li>determines whether an element is hidden from accessibility APIs (<code>aria-hidden='true'</code>) or not (<code>'false'</code>);</li><li> is useful for hiding decorative or duplicative content (e.g. decorative icon next to text).</li></ul>
</dd>
</dl>
<hr />
<p>In addition to the above, we can apply multiple CSS properties within a rule set to <strong>hide an element visually while keeping it screen-reader accessible.</strong> An example of that would be providing text for assisitive technologies only that can’t be displayed visually. <a href="https://www.sarasoueidan.com/blog/accessible-icon-buttons/">Accessible icon buttons</a> are a common and good example of that. Typically, the styles are applied using a utility class:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/*******************************************************************************\<br /><span class="highlight-line"> * *</span><br /><span class="highlight-line"> * Visually hide any element (mostly text) accessibly. *</span><br /><span class="highlight-line"> * Support includes IE9+ *</span><br /><span class="highlight-line"> * Source: https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html *</span><br /><span class="highlight-line"> * *</span><br /> *******************************************************************************/</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This utility class shrinks an element into a 1px square, hiding any overflow, and absolutely positioning the element to remove any trace of it from the normal document flow. This utility class is ideal for providing screen reader-only <em>text</em>.</p>
<hr />
<p>After going over all of the above techniques, I always ask my talk and workshop attendees how they would hide a native checkbox while ensuring it remains screen reader-accessible. Since we want to make sure the checkbox remains screen-reader accessible, we rule out all of the rules that hide it from screen readers. This left us with the two most frequent answers:</p>
<ol>
<li>Hide the checkbox using the <code>.sr-only</code> class, because it seems like the perfect solution because it hides the checkboxes visually whilst keeping it accessible to screen readers, and this is what most articles online currently use.
<br /><br />and<br /><br /></li>
<li>Move the checkbox off-canvas, hiding it outside of the viewport using absolute positioning. This, too, removes the checkbox from view but does not remove it from the accessibility tree.</li>
</ol>
<p>It is true that both of these techniques hide the checkbox visually and it will still be accessible by a screen reader, <strong>but neither of these techniques are inclusive of users navigating by touch.</strong></p>
<h2 id="hiding-the-checkboxes-inclusively" tabindex="-1">Hiding the checkboxes inclusively</h2>
<blockquote>
<p>Touch interface screen readers allow users to run their finger over the screen to hear what is directly underneath. This provides the user with a quick sense of an entire interface. — <a href="https://material.io/design/usability/accessibility.html#understanding-accessibility">Material Design Acessibility Guidelines</a></p>
</blockquote>
<p>Screen readers on Android touch devices give users multiple ways to navigate a screen. One of these ways is <a href="https://motorola-global-en-uk.custhelp.com/app/answers/indevice_detail/a_id/104169/p/30,6720,9299"><strong>exploring by touch</strong></a>. Rob Dodson has <a href="https://www.youtube.com/watch?v=0Zpzl4EKCco&list=PLNYkxOF6rcICWx0C9LVWWVqvHlYJyqw7g&index=27">a great screencast</a> covering the basics of navigating a page using TalkBack on Android that I recommend watching for a live demo.</p>
<p>Exploring by touch means that a mobile screen reader can explore pages on touch screens with haptics — they literally move their finger on the page looking for interactive elements.</p>
<p>When you create a checkbox (or any other interactive element, for that matter), the user will expect to find that checkbox by touching the screen where they expect it to be. So the way you hide the checkbox determines whether touch screen reader users will be able to find it or not.</p>
<p>As you can possibly imagine now, hiding the checkbox off canvas (outisde of the viewport area) will make it inaccessible to them, because they won’t find it within the viewport bounds as drag their finger around. Similarly, shrinking the checkbox to 1px will also make it very difficult to find and touch. So, <strong>while the <code>sr-only</code> utility class is great for visually-hiding static content (e.g. text), it should not be used to hide interactive elements.</strong></p>
<p>So, how <em>do</em> you hide a checkbox inclusively? The answer is: hide it visually but make sure it is still ‘physically’ present where it would naturally be present so that touch users can find it with haptics.</p>
<p>Technically speaking this means:</p>
<ol>
<li>remove the checkbox from the page flow using <code>position: absolute</code> so that it doesn’t take up any unwanted space (visually),</li>
<li>position it (within the label) <strong>making sure it is positioned <em>on top</em> of the image that is visually replacing it,</strong></li>
<li><em>optional:</em> set its dimensions to match those of the SVG,</li>
<li><strong>visually hide it by making it transparent</strong> with <code>opacity: 0</code>,</li>
</ol>
<p>Here is a video demo’ing the above steps.</p>
<figure class="wide">
<video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/inclusive-checkbox.mp4" width="auto" style="width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video>
</figure>
<p><small>Note that I’ve already styled the SVG to convey state in this demo, which is what I’ll cover in the next section.</small></p>
<br />
<p>The CSS that handles the positioning and hiding of the checkbox looks like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.c-custom-checkbox</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* create a postioning context for the checkbox within the label */</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* other label styles here */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.c-custom-checkbox input[type="checkbox"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* remove the checkbox from flow */</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* hide it visually */</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* tweak size and position if needed */</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* position it within the label, on top of the SVG */</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* sometimes you may need to add z-index */</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<p>So the checkbox is technically still there where it should be, it is still interactive, it is fully accessible, but it is visually hidden so it can be replaced with a more styleable alternative: the SVG.</p>
<!-- _**Note** I’ve replaced the `0` opacity value with `0.0001` (basically a very small value that's almost zero but not quite) to make sure ChromeVox announces the checkbox because otherwise [it does not read content with `opacity: 0`](https://github.com/michalsnik/aos/issues/397). Hat tip to [Carolyn MacLeod](https://twitter.com/carmacleod) for the heads up and (once again to) my friend Scott for [his findings](https://scottaohara.github.io/a11y_styled_form_controls/src/checkbox/index.html)._ -->
<h2 id="styling-the-svg-accessibly" tabindex="-1">Styling the SVG accessibly</h2>
<p>Since we’re hiding the native checkbox, we will need to substitute for the checked and unchecked states visually, as well as the focus styles.</p>
<p>The SVG is placed right after the checkbox in the DOM, so we can select it using the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator">adjacent siblings selector</a>, and style it based on the checkbox’s state. So when the checkbox receives focus, we display the focus outline on the SVG:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* visually show focus outline when the SVG receives focus */</span></span><br /><span class="highlight-line"><span class="token selector">.c-custom-checkbox input[type="checkbox"]:focus + svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> outline<span class="token punctuation">:</span> 3px solid #E55360<span class="token punctuation">;</span></span><br /><span class="highlight-line"> outline-offset<span class="token punctuation">:</span> 2px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* hide the focus styles for mouse users */</span></span><br /><span class="highlight-line"><span class="token selector">.c-custom-checkbox input[type="checkbox"]:focus:not(:focus-visible) + svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> outline<span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<p>The focus style can be anything you want, as long as it’s very clear and visually accessible. Similarly, you can add disabled state styles using the <code>:disabled</code> seletor.</p>
<p>To mimic checking/unchecking the checkbox in the SVG, we show/hide the checkmark inside it, and change the background color:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* basic styles for the svg */</span></span><br /><span class="highlight-line"><span class="token selector">.c-custom-checkbox svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* set SVG dimensions in ems; i.e. relative to the font size so that it scales with the size of the text in the label */</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* apply a transition to the elements inside the svg */</span></span><br /><span class="highlight-line"> <span class="token selector">*</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> all 0.1s linear<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* style changes inside the svg when the checkbox is checked */</span></span><br /><span class="highlight-line"><span class="token selector">.c-custom-checkbox input[type="checkbox"]:checked + svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.checkbox__bg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">fill</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--checked-state-bg-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">stroke</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--checked-state-bg-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">.checkbox__checkmark</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">stroke</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--checked-state-checkmark-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<p>While you’re at it, you’ll want to take it further and optimize it for Windows High Contrast Mode:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.c-custom-checkbox svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">-ms-high-contrast</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.checkbox__bg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">stroke</span><span class="token punctuation">:</span> windowText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">-ms-high-contrast</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.c-custom-checkbox input[type="checkbox"]:checked + svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.checkbox__bg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">fill</span><span class="token punctuation">:</span> windowText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">.checkbox__checkmark</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">stroke</span><span class="token punctuation">:</span> highlight<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<p>One of the many benefits of using an inline SVG is that we have real elements (checkmark and square) with real borders (strokes) that we can flexibly style, so we don’t rely on background images and colors alone to create and convey state/behavior, because background images, colors, and effects like drop shadows are normally overridden in user-controlled environments. This is also why I normally recommend using a <em>real</em> outline versus a fake outline created using <code>box-shadow</code>.</p>
<p>And here is a live demo:</p>
<p class="codepen" data-height="600" data-default-tab="result" data-slug-hash="39314a8271e0a7268ac79144393edb24" data-user="SaraSoueidan" style="height: 571px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span><a href="https://codepen.io/SaraSoueidan/pen/39314a8271e0a7268ac79144393edb24">See the pen</a> (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2 id="adding-delight-using-svg-animations" tabindex="-1">Adding delight using SVG animations</h2>
<p>Using animated SVGs is one of my favorite ways of adding delight to otherwise boring user interfaces, particularly form controls. Checkboxes and radio buttons are a great example of controls that could benefit from more fun interactions. In 2013, Codrops published <a href="https://tympanus.net/codrops/2013/10/15/animated-checkboxes-and-radio-buttons-with-svg/">a collection of playful experiments</a> using SVG path animations (a.k.a. <a href="https://jakearchibald.com/2013/animated-line-drawing-svg/">the line drawing technique</a>) to create more delightful checkboxes and radio buttons. But the Codrops examples were merely a proof-of-concept for animation and were not optimized to be accessible. If you want to use such animations in your UIs today, you’ll want to make sure you hide the checkboxes using the technique presented in this article.</p>
<img src="https://sarasoueidan.com/assets/images/codrops-animated-checkboxes.gif" alt="a collection of animated checkboxes and radio buttons found on codrops" />
<p>Here is a quick proof of concept of an accessible animated checkbox:</p>
<p class="codepen" data-height="600" data-default-tab="result" data-slug-hash="Jowwde" data-user="SaraSoueidan" style="height: 571px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span><a href="https://codepen.io/SaraSoueidan/pen/Jowwde">See the pen</a> (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2 id="wrap-up" tabindex="-1">Wrap-up</h2>
<p>There are several ways to hide an element in CSS and HTML. You can hide an element both visually and from screen readers, only visually, or only from screen readers.</p>
<p><strong>When you hide an interactive element, make sure you choose a hiding technique that keeps it screen reader-accessible, position it <em>on top</em> of whatever is visually replacing it so that a user navigating by touch can find it where they expect to, and then make it transparent.</strong></p>
<h2 id="references-and-recommended-reading" tabindex="-1">References and Recommended Reading</h2>
<ul>
<li><a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">Inclusively Hidden</a></li>
<li><a href="https://24ways.org/2018/inclusive-considerations-when-restyling-form-controls/">Inclusive Considerations When Restyling Form Controls</a> + <a href="https://scottaohara.github.io/a11y_styled_form_controls/src/checkbox/index.html">more by Scott O’hara</a></li>
</ul>
Optimizing keyboard navigation using tabindex and ARIA
2020-06-09T17:01:45Z
https://sarasoueidan.com/blog/keyboard-friendlier-article-listings/
<p class="deck">
The faster the user can navigate your UI the better. The faster they can get to the content they need, the better. Therefore, the less steps they have to go through, the more efficient their overall experience with the UI will be. And this applies to keyboard tabbing, too: the less tabs the user needs to stop at while navigating, the faster they are to get to where they need. The way we mark up our content has a direct effect on the user’s experience. But we can drastically improve the UX by specifically optimizing the markup for keyboard users.
</p>
<h2 id="clicks.-taps.-tabs.-steps." tabindex="-1">Clicks. Taps. Tabs. Steps.</h2>
<p>If there’s one thing working with UX designers has taught me it’s that the less steps the user needs to go through their journey, the better. As such, they usually design experiences by making sure fewer clicks are needed to get the user to the end of their journey.
As a design engineer, when talking about “steps” and “clicks”, my brain can’t help but think of taps and tabs, too. Because a user could be interacting with the UI in several ways, not just using a mouse. Taps are practically touch equivalents of clicks, so I assume the experience would be as optimized for those as it is for non-touch. But the experience for keyboard users is usually a more than a little different.</p>
<p>As a design engineer, my job is to weigh in and provide consultancy and recommendations to make sure the UI design works for users in different contexts. This means that I provide feedback to make sure keyboard user experiences, among others, are also considered. Designs are often tweaked if and when needed based on our UX discussions. But often times, us developers can make optimizations and improvements to accessibility and UX that don’t require design discussions and/or permissions. And when we can, we should.</p>
<hr />
<p>A user navigating and interacting with a UI using a mouse can usually move across the page with little effort — they literally move the cursor to where they want to. The mouse cursor ”hovers” <em>above</em> a page, so to speak. Unless you use a design anti-pattern, there are rarely any obstacles in their way, because most designs are usually optimized for their interactions. It doesn’t matter how many links there are on the page, they can go straight to the link they want, click it, and move on.</p>
<p>A keyboard user, on the other hand, can’t go anywhere without going through a series of tab stops that <em>you</em> set for them. If they were a person standing infront of stairs, a keyboard user needs to walk up the stairs to get to their destination (compared to a mouse user who wears a pair of magical flying shoes or uses a Hoverboard to <em>fly</em> up the stairs 😅). The more steps the stairs have, the more steps they need to climb, the longer their journey, and the more cumbersome it can be. There are no shortcuts to go where they want to more quickly and efficiently… <em>unless</em> you <em>provide</em> them with shortcuts. I’ll elaborate on that in another post. In this post, I want to talk about markup and how it can sometimes be optimized for more efficient keyboard navigation. As an example, I want to talk about article listings.</p>
<hr />
<p>Blogs, online magazines and other publications will normally display lists of articles or posts. Browsing such publications, often you’ll notice a familiar pattern in most of those listings: a post entry has a typical anatomy normally consisting of:</p>
<ul>
<li>a thumbnail,</li>
<li>a post title,</li>
<li>author name,</li>
<li>post tags,</li>
<li>an excerpt or description,</li>
<li>and a “read more” link,</li>
</ul>
<p>give or take one or more elements. So the post may include all of the above elements minus the thumbnail, for example. Or it might be composed of a thumbnail, a title, and a description only. It could also include a link to the comments section on the full article page. And so on.</p>
<p>Typically, the thumbnail, post title and Read More link all link to the same page: the full article page, while the author name and list of tags post to their corresponding pages. If you’re a sighted mouse user and you want to read the full post, you click on either the thumnail, the title, or the Read More link to get there. You may even be able to click on the whole post <a href="https://www.sarasoueidan.com/blog/nested-links/">if it is marked up for it</a>.</p>
<p>But how does this post anatomy affect a keyboard user?</p>
<h2 id="navigating-a-list-of-posts-with-a-keyboard" tabindex="-1">Navigating a list of posts with a keyboard</h2>
<p>If you use a keyboard to navigate a page containing a typical list of posts, you’ll notice that you sometimes need to tab through the same link two or three times in a row. To demonstrate, I recorded the exeprience of tabbing through the <a href="https://www.nytimes.com/">New York Times</a>, <a href="https://medium.com/">Medium</a>, and <a href="https://forbes.com/">Forbes</a> homepages:</p>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/forbes.mp4" width="auto" style="width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>Posts on the Forbes homepage contain three links per post: the the thumbnail, the post title, and an icon-only version of a “Read More” link. Clicking on any of these links takes the user to the full article page corresponding to this post. So, <strong>a user navigating using keyboard practically needs to tab through the same link three times in a row</strong> to continue navigating to the next post and whatever content comes after. The more posts there are, the longer the tabbing journey will be.</figcaption>
</figure>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/nyt.mp4" width="auto" style="width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>The New York Times homepage features a list of posts, each containing two focusable elements / links: a thumbnail, and a link that includes the text for both the title and post description. <strong>Both of these links take the user to the full article page.</strong></figcaption>
</figure>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/medium.mp4" width="auto" style="width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>On the Medium homepage, each post is made up of five links, three of which link to the full article page: the post thumbnail, the title, and the post description. Tabbing through these posts means the user needs to tab through the same link three times in a row.</figcaption>
</figure>
<p>If you use a keyboard a lot, you’ll probably start to notice how redundant it is to tab through the same link multiple times in a row. If you’re wanting to get to a section that comes after the article listings on that page, you’ll need to tab quite a lot before you get there.</p>
<p>When a list of posts is designed like with such a structure in mind, it is usually optimized for <strong>a sighted mouse/touch user</strong>. Such a user would have a generous collection of elements to click or tap, all leading to the same page that they probably eventually want to visit.</p>
<p>But this design is not optimized for keyboard users. When a keyboard user needs to tab through the same link two or three times in a row they are <em>slowed down</em> because their journey becomes literally 200% or 300% longer than it could/should be.</p>
<p>Depending on the post structure and anatomy, we can sometimes drastically improve the keyboard user experience with very little dev effort.</p>
<h2 id="optimizing-keyboard-navigation-using-tabindex-and-aria-hidden" tabindex="-1">Optimizing keyboard navigation using <code>tabindex</code> and <code>aria-hidden</code></h2>
<p>Last Fall, I had the opportunity to work on an upcoming redesign of an online publication. I was hired to build a brand new front-end foundation for the design. I put a lot of emphasis on making sure the new design is implemented as accessibly and inclusively as possible. So, naturally, I wanted to make sure the experience is optimized for keyboard users as well.</p>
<p>Being an online publication, it contains hundreds of articles, and those articles are displayed as a list of posts on the homepage and other pages of the site.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/post-list.png" alt="A screenshot of three blog post entries with a structure similar to the one mentioned earlier." />
<figcaption>There were more than a dozen article entries on the publication’s homepage. Each entry consisted of an image thumbnail (mostly decorational), a title, an author name, a description (without links), and a Read More link. This means that each entry contained three links to the full article page, and one link to the author’s page.</figcaption>
</figure>
<p>In the process of testing a component at the bottom of the homepage for accessibility, I realized that tabbing through the page to get to that component took longer than I would like it to, due to the large number of post entries on the page. Sure, this tabbing experience is the typical experience on any and all similar Web sites I’ve ever visited. But <strong>just because it is the typical experience, doesn’t mean it can’t be improved upon.</strong></p>
<p>After a long discussion about usability and accessibility, I made a small addition to the markup that improves keyboard navigation <strong>by reducing the number of tabbable links</strong>. <strong>The number of links does not change; the tabbability of a link does.</strong> This means that a user navigating using a keyboard would only tab through one link to the full article page in each post instead of three, thus making the keyboard experience faster and more efficient overall.</p>
<p>The implementation is simple:</p>
<ol>
<li>Prevent a link from being tabbed by using <code>tabindex = "-1"</code>, and</li>
<li>hide said link from screen readers using <code>aria-hidden = "true"</code>, because you don’t want a screen reader to expose a link the user won’t be able to interact with.</li>
</ol>
<p>I applied this to the thumbnail images because they are not used to convey any particularly relevant information in an entry (they are more like decorational cover photos), and to the Read More links. Only the post title remains tabbable, as well as the link to the author page. So instead of having four tab stops in each entry, a user now has two. Thus tabbing through the long list of posts is going to be two times faster than what a typical experience would have been.</p>
<p>The markup for each article entry looked something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post ...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/path/to/full-article/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post__thumb<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- img thumbnail... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post__content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/path/to/full-article/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post__title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Inspirational Website of the Week: Dean Bradshaw<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post__excerpt<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/path/to/full-article/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post__more-link<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>7<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 16 7<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment"><!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>The following are two live codepens that you can tab through using a keyboard to see the difference between the typical default experience and the experience with the skipped links:</p>
<p class="codepen" data-height="1000" data-theme-id="3617" data-default-tab="result" data-user="SaraSoueidan" data-slug-hash="fff7257ab01a4627c02b500cb9b2db21" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Demo for blog post: Optimizing keyboard navigation using tabindex and ARIA">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/fff7257ab01a4627c02b500cb9b2db21">
Demo for blog post: Optimizing keyboard navigation using tabindex and ARIA</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<p class="codepen" data-height="1000" data-theme-id="3617" data-default-tab="result" data-user="SaraSoueidan" data-slug-hash="7ed44ee6163a42500e2928b0cbe5c2cc" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Demo for blog post: Optimizing keyboard navigation using tabindex and ARIA">
<span>See the Pen <a href="https://codepen.io/SaraSoueidan/pen/7ed44ee6163a42500e2928b0cbe5c2cc">
Demo for blog post: Optimizing keyboard navigation using tabindex and ARIA</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<h2 id="one-size-does-not-fit-all." tabindex="-1">One size does <em>not</em> fit all.</h2>
<p>Not all article listings are equal. In the examples above, there weren’t many links between each of the consecutive full article page links. In each of the examples, the post’s excerpt is short and does not contain any links, and there are no lists of tags following the post title and separating the title from the Read More link.</p>
<p><small>Depending on how you set up your CMS or SSG to generate excerpts, they could be generated as plain text or full markup. For example, on this blog, the excerpts are generated as plain text, so they don’t contain any links. On codrops, the excerpts aren’t really excerpts — they are custom descriptions of an article, so, by design, they also don’t contain any links. But not all post excerpts or descriptions come without links.</small></p>
<p>When there <em>are</em> <em>several</em> links between the title and the Read More link, it becomes <em>important</em> for the user to be able to use the Read More link. To demonstrate, I recorded the process of tabbing through <a href="https://lea.verou.me/">Lea Verou’s Web site</a>, where she shows a longer post excerpt per entry and containing quite a few links in some cases.</p>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/lea-verou.mp4" width="auto" style="width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>Tabbing through a post entry on Lea Verou’s Web site. If the Continue Reading link were not there on Lea’s post, a keyboard user would need to tab all the way back to the title to visit the full article page. In such a case, a Read More link does provide value and should not be deactivated.</figcaption>
</figure>
<p>Because there are quite a few links after the post title in each entry, the <em>Continue Reading</em> link in Lea’s posts is essential for the keyboard experience. The link is a shortcut to the full article’s page now; had it not been there, or had it been skipped, the user would need to tab all the way back up to the post title to visit the full article, which would have been nonsensical. This is an example of when you do <em>not</em> want to use <code>tabindex</code> and ARIA to skip over a link because it would <em>worsen</em> the user experience.</p>
<p>A few years back, I took a different approach to implementing post entries when I built <a href="https://smashingmagazine.com/">Smashing Magazine</a>’s front-end foundation. The posts were designed to be “cards”, and cards come with their own implementation considerations that I documented in my <a href="https://www.sarasoueidan.com/blog/nested-links/">previous post about nested links</a>.</p>
<p>Each design comes with its own usability considerations that need to be taken into account. In order to improve an experience, you need test the design using a keyboard, and with real users if possible — they can usually provide a lot of insight that we designers/developers might miss.</p>
<h2 id="to-sum-up%3A" tabindex="-1">To sum up:</h2>
<ul>
<li>Not all your users use a mouse to navigate your pages.</li>
<li>The way you mark up your elements has a direct effect on how people interact with them.</li>
<li>Links are focusable by default, but it might make sense to skip certain links <em>if the design and user experience allows it</em>.</li>
<li>A good rule of thumb for similar cases is that <strong>if you have multiple consecutive links to the same page, there is probably a chance to improve keyboard navigation by skipping some of those links to reduce the number of tab stops to one.</strong> The less tab stops, the better, as long as it does not worsen or compromise on other aspects of usability.</li>
<li>There is no one rule fits all. One approach might work for a design but not for another.</li>
</ul>
<p>And, once again: each design comes with its own challenges and considerations. Take those into account, weigh your options, test with real users, use a keyboard, and make optimizations when and where appropriate. <strong>At the end of the day, it should always be about the <em>user</em> experience.</strong></p>
Global and Component Style Settings with CSS Variables
2020-06-01T00:00:00Z
https://sarasoueidan.com/blog/style-settings-with-css-variables/
<p class="deck">Ever since I learned about CSS Variables a few years back, my absolute favorite feature has been the ability to scope variables to components. But to be honest, I haven't been putting this feature to much use over the years, <em>until</em> I created my own pattern library last year to speed up prototyping and client work. That’s where scoped CSS variables really shined for me. So I want to share my favorite two ways to use CSS Variables to organize and maintain styles in my projects today.</p>
<p>Over the last few months, I’ve started approaching the way I organize and manage my CSS differently…</p>
<h2 id="global-project-settings" tabindex="-1">Global Project Settings</h2>
<p>Today, at the beginning of each project, I create a <code>_settings.scss</code> stylesheet. This stylesheet contains the <em>global settings</em> for the project. These settings are usually derived from the design style guide provided by the design team I’d be working with.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/style-guide-settings.png" alt="partial screenshot of a style guide showing values for color swatches and box shadow styles" />
<figcaption>An example of visual settings defined in a style guide for my current client project. The style guide contains color swatches, brand colors, interaction and UI colors, font styles, type scales, spacing scales, icons, etc. I used the style guide as a starting point in my CSS, as I created and defined the global project styles by deriving their values from their visual equivalents in the style guide.</figcaption>
</figure>
<p>Just like the style guide contains settings for visual styles like colors, box shadow styles, font styles, type scales, etc., the <code>_settings</code> stylesheet contains variables that serve as the code equivalent of those settings, and that are used across the CSS to maintain visual consistency across the project.</p>
<figure>
```css
:root {
/* UI Colors */
--primary-hue: 12;
--color--primary: hsl(var(--primary-hue), 100%, 44%);
--color--primary--hover: hsl(var(--primary-hue), 100%, 39%);
--color--primary--active: hsl(var(--primary-hue), 84%, 30%);
<p>/* … */</p>
<p>–border-color: #ebebeb;</p>
<p>/* Box Shadows */
–shadow-01: 0px 2px 4px rgba(37, 37, 37, 0.1);
–shadow-02: 0px 4px 8px rgba(37, 37, 37, 0.1);
–shadow-03: 0px 8px 16px rgba(37, 37, 37, 0.1);
–shadow-04: 0px 16px 24px rgba(37, 37, 37, 0.1);
–shadow-05: 0px 24px 32px rgba(37, 37, 37, 0.1);</p>
<p>/* … */</p>
<p>}</p>
<pre><code>
<figcaption>An example of global style settings defined in a <code>_settings.scss</code> style sheet in my current client project.</figcaption>
</figure>
```css
.card {
/* ... */
box-shadow: var(--shadow-01);
border: 1px solid var(--border-color);
transition: box-shadow .2s linear;
&:hover,
&:focus {
box-shadow: var(--shadow-03);
}
}
</code></pre>
<p>If at any point during the project any of those settings need to change, I know exactly where to go make that change, and I know it will propagate consistently throughout my entire system.</p>
<hr />
<p>In addition to these settings, I’ve been finding the most value and convenience in using CSS variables to define local, component-scoped styles…</p>
<h2 id="quicker-prototyping-with-%E2%80%9Cskeleton%E2%80%9D-components" tabindex="-1">Quicker Prototyping with “Skeleton” Components</h2>
<p>Over the years, and in the interest of saving myself time and speeding up prototyping ideas and client work, I’ve created a library of UI and design patterns that I find myself needing to recreate on most of my projects. The library now contains a growing collection of easily reusable UI patterns that I can reliably copy-paste into my projects when I need them. Each pattern is progressively enhanced using modern CSS and JavaScript, and is cross-browser and cross-platform accessible from the ground up.</p>
<p>Since I created the library as an ‘internal’ project, it currently lives in a private Github repository, and behind a password on the <code>.dev</code> domain of my site.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/skeleton.png" alt="screenshot of the Skeleton component library" />
</figure>
<p>I named the library ‘Skeleton’, and I built it with <a href="https://fractal.build/">Fractal</a>. I’ve been using Fractal for a couple of years now. I chose it over other pattern library tools because it fit my needs perfectly — I wanted a tool that was unopinionated and flexible enough to allow me to set up and structure my project the way I wanted to. Fractal fit the description perfectly because it is agnostic as to the way I develop or the tools I use. And I was further sold on it after reading about <a href="https://www.smashingmagazine.com/2018/07/pattern-library-first-css/">how Rachel Andrew uses it to manage CSS</a> using a pattern-first approach, which was <em>exactly</em> what I was going for. I particularly love that <q>components can be nested into folders to make it easier to locate particular components, and how you structure the folders is completely up to you.</q></p>
<p>I organize my patterns so that each pattern lives in its own directory, containing the component’s HTML template, CSS, and vanilla JavaScript files, along with any additional Fractal-specific assets (such as config files).</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/skeleton-folder-structure.png" alt="partial screenshot of the folder structure in my component library" />
</figure>
<p>Using this structure, each of my patterns is self-contained. And I can include and concatenate the pattern’s styles and scripts in my projects as I need.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@import</span> <span class="token string">"accordion"</span><span class="token punctuation">;</span></span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@import</span> <span class="token string">"modal"</span><span class="token punctuation">;</span></span></span></code></pre>
<p>To quote Tyler Sticka in <a href="https://cloudfour.com/thinks/tips-for-portable-patterns/">“Tips for Portable Patterns”</a>: <q>Patterns, like songs, are easier to remix when each master track is separated.</q></p>
<hr />
<p>My goal from creating this library is to create a tool that allows me to prototype faster, and that’s flexible and efficient enough to use across different projects. And since patterns are usually styled differently across my projects, I wanted a way to simplify the process of customizing or “configuring” them for each project. Enter CSS Variables.</p>
<h2 id="scoped-component-settings" tabindex="-1">Scoped Component Settings</h2>
<p>Because I don’t want to spend a lot of time overriding and undoing styles when I use a pattern in a new project, I created this library with the components having little to no styling by default — mostly white (no colors), minimal spacing, and only borders where visually appropriate. So the patterns literally look like skeletons of sort, hence the name. Now when I need to use one of these components, I have little CSS to override before they’re ready to be plugged into the new project.</p>
<p>For each pattern, I’ve found myself modifying the same properties whenever I needed to use it — like the font, colors (text, background, border), box shadow, spacing, etc. So I figured it would be useful and time-saving if I created variables for those properties, define those variables in the ‘root’ of the component, and ‘pass in’ the values for these variables when I use the pattern as I need. This way I can customize or theme the component by changing the property values in one rule set, instead of having to jump between multiple ones to do so.</p>
<p>I use variables to abstract all styles that I usually need to override. So every property that changes across projects is usually “promoted” into a variable. Then, if, at any point during the project, I need to tweak a pattern’s style(s), I know exactly where to do it. This makes the styles for each pattern more readable and more maintainable, which is even more important for when someone else needs to modify the CSS.</p>
<p class="note">This approach works quite well with my approach to organizing my CSS files. I like to organize my CSS into separate style sheets per patterns, that includes all the pattern’s styles and responsive behavior. There will be exceptions to this rule... for example, styles for “atoms” (like buttons, input fields, etc.) that are re-used across patterns are defined in one style sheet. And even then, when an atom’s styling gets a little complex (e.g. styling a custom file upload input), I may create a separate style sheet for that. I’ll go into a little more detail about the way I organize my CSS in another article.
</p>
<h3 id="example" tabindex="-1">Example</h3>
<p>Like most of you, I find myself creating a <code><select></code> element in almost all of my projects. As of last year, I use <a href="https://www.filamentgroup.com/lab/select-css.html">Scott Jehl’s cross-browser styling technique</a>. I find that I mostly need to change the icon, border color, border radius, and background color the most. In every project, I also set a focus outline for interactive elements. The outline color also varies across projects.</p>
<p>So, I promoted these properties into variables. For each property, I set a default or empty value and change that value when I use the component in a new project as I need.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.c-custom-select</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">--icon</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"data:image/svg+xml;charset=US-ASCII,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2232%22%20height%3D%2232%22%20viewBox%3D%220%200%2032%2032%22%3E%0A%3Cpath%20fill%3D%22%23777%22%20d%3D%22M9.914%2011.086l-2.829%202.829%208.914%208.914%208.914-8.914-2.828-2.828-6.086%206.086z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--icon--disabled</span><span class="token punctuation">:</span> <span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--border-color</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--border-color--disabled</span><span class="token punctuation">:</span> <span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--color--disabled</span><span class="token punctuation">:</span> <span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--border-radius</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--background-color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--gradient-background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to bottom<span class="token punctuation">,</span> #ffffff 0%<span class="token punctuation">,</span> #e5e5e5 100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--outline-color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>265<span class="token punctuation">,</span> 50%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--padding</span><span class="token punctuation">:</span> .5em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I like to think of this rule set as a settings object for the component. It makes theming the component faster by passing in the values you want.</p>
<p>The rest of the rule sets for the select component contain fixed rules or styles that most likely won’t change across projects:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"> <span class="token property">-moz-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--padding<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding-right</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--padding<span class="token punctuation">)</span> * 3<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.3<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid <span class="token function">var</span><span class="token punctuation">(</span>--border-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--border-radius<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--background-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--icon<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--gradient-background<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">,</span> repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-position</span><span class="token punctuation">:</span> right <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--padding<span class="token punctuation">)</span> * 1.5<span class="token punctuation">)</span> top 50%<span class="token punctuation">,</span> 0 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 1em auto<span class="token punctuation">,</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.c-custom-select::-ms-expand</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.c-custom-select:focus</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 3px <span class="token function">var</span><span class="token punctuation">(</span>--outline-color<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 3px -moz-mac-focusring<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.c-custom-select:focus:not(:focus-visible)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.c-custom-select:disabled, .c-custom-select[aria-disabled=true]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--color--disabled<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--icon--disabled<span class="token punctuation">)</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to bottom<span class="token punctuation">,</span> #ffffff 0%<span class="token punctuation">,</span>#e5e5e5 100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.c-custom-select:disabled:hover, .c-custom-select[aria-disabled=true]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--border-color--disabled<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<h2 id="moving-forward" tabindex="-1">Moving Forward</h2>
<p>As much as I’d love to do this in all of my projects, unfortunately I can only use this approach to styling and managing component CSS in my own projects at the time being. The reason for that is that most of my clients still need to support at least one or two versions of IE, which have no support for CSS Variables. And while there is <a href="https://github.com/aaronbarker/css-variables-polyfill">a polyfill</a> for CSS Variables, the polyfill only provides support for variables defined on the root HTML element. Depending on the level of support and optimization we need for the IEs, I currently reach for the polyfill and use CSS Variables at least for defining global project styles.</p>
<p>I see myself using this approach to style management in the future in combination with <a href="https://www.webcomponents.org/">Web Components</a> to create overall more portable patterns. And hopefully, some time in the future we’ll have container queries to make these components <em>truly</em> self-contained.</p>
<p>As for <strong><em>Skeleton</em></strong>, it still has a long way to go. My goal is to create a custom Fractal theme to go with it. I haven’t yet found a good and complete guide on how to do this, especially since I’m looking to create a theme from scratch so I have full control over its styles. If you know me, you know I’ll probably write that guide once I figure out how to create a theme from scratch.</p>
<p>I’m also constantly working on adding more patterns and variations of patterns. And I plan on including a Snippets section which includes code snippets I also frequently use in projects, such as JavaScript patterns and functions. And of course, I want to go heavy on documentation, including adding posts about best practices such as perf optimization techniques, icon and images best practices, etc., and I’ve already started drafting most of these ideas.</p>
<p>I may or may not turn the library into a shareable product at some point in the future. But this is not something on its roadmap at the moment. For now, I like the idea of this being a small personal project with no strings attached. That said, I would love to publish the library as an npm package (for private use to start). I’m not familiar with how to do this yet. But I found <a href="https://cloudfour.com/thinks/how-to-distribute-a-pattern-library-as-an-npm-package-from-a-private-git-repo/">an article on CloudFour</a> that dives into doing this exact thing, so that will probably be my starting point. And I will definitely document my process as I figure my way around doing it.</p>
<hr />
<h3 id="update%3A-june-8th-2020" tabindex="-1">Update: June 8th 2020</h3>
<p><strong>Heads up:</strong> using scoped CSS variables also has performance benefits because setting & modifying variables defined on a global scope can be expensive & have performance pitfalls due to large amounts of style recalculations. Lisi Linhart has <a href="https://lisilinhart.info/posts/css-variables-performance">an excellent writeup</a> about the performance of CSS variables as well recommendations to avoid any performance hits.</p>
</figure>
What a Year of Learning and Teaching Accessibility Taught Me
2019-12-25T00:00:00Z
https://sarasoueidan.com/blog/what-accessibility-taught-me/
<p class="deck">
<small><em>(This article was originally published on <a href="https://sarasoueidan.com/blog/what-accessibility-taught-me/">24accessibility</a>)</em></small>.
<br />
<br />
A few years ago I didn't know what the term "accessibility" meant. I built Web sites that were partially inaccessible <strong>because I didn't know better</strong>. Fast forward to today, I know enough to be able to <a href="https://sarasoueidan.com/blog/">write, <a href="https://sarasoueidan.com/speaking/">speak and run workshops</a> on accessibility, helping others build more a accessible and inclusive Web. Much like everyone else in our field, I am still learning. But since I started, I learned a lot of valuable lessons and core values that drive my work today. Here are a few of them.
</a></p>
<h3 id="semantic-html-is-the-foundation-of-a-truly-accessible-web." tabindex="-1">Semantic HTML is the foundation of a truly accessible Web.</h3>
<p>Semantic HTML is the universal language that all devices accessing the internet understand. It is the language you use to communicate your content to these various devices, including but not limited to browsers, reading apps, screen readers, smart watches, and more.</p>
<p>HTML is semantic, or in other words, it is descriptive and provides meaning — each HTML element describes the type of content it presents. So if you have a heading, you use a heading element. If you have a paragraph, you use a <code><p></code> tag. In other words, it means using the correct HTML elements for their correct purpose.</p>
<p>By using correct elements, your document content will have conveyable <strong>structure</strong> and <strong>meaning</strong>.</p>
<p>Structure is important because it helps interoperability. Interoperability is the ability of different systems, devices, applications or products to connect and communicate in a coordinated way, without effort from the end user. In other words, it allows more devices to interpret and access your content, including devices that will show up in the future.</p>
<p>Structure helps applications like reading apps and reader modes (such as Safari’s reader mode) as well as environments like Windows High Contrast Mode understand your content and style it in ways that improve the user experience. This is only possible when the proper HTML semantic elements are used, such as <code><article></code>, <code><h1></code>, <code><ul></code>, <code><aside></code>, <code><nav></code>, among <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element">many others available in HTML5</a>. These elements describe the type of content they contain. Without them, these applications wouldn’t be able to tell what that content is, and therefore won’t be able to style it properly. This increases the risk of making the content less accessible, if not completely inaccessible.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/semantics-and-reader-modes.png" alt="Mobile screenshots of a Web page in the Instapaper app." />
<figcaption>A Web page by Mandy Michael demonstrating how an article is styled in the app Instapaper when that article is marked up using semanticless divs (left) and semantic HTML elements (right). (<a href="https://medium.com/@mandy.michael/building-websites-for-safari-reader-mode-and-other-reading-apps-1562913c86c9">Source</a>)</figcaption>
</figure>
<p>Structure is also important because it allows your users to navigate your content more efficiently. Screen reader users rely on proper document structure to jump to areas of the page they need more quickly. They do that using various quick/hot keys — power commands but for screen reader users. If you don’t use proper landmarks (exposed to screen readers via semantic HTML elements like <code><nav></code> and <code><main></code> and <code><header></code>), screen reader users may not be able to efficiently navigate the page and would have to search for their areas of interest more tediously.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/rotor-landmarks.png" alt="Screenshot of macOS's VoiceOver rotor open on sarasoueidan.com" />
<figcaption>VoiceOver’s rotor menu exposes all landmarks available on a Web page, allowing the user to easily jump to the part of the page they need.</figcaption>
</figure>
<p>Headings also provide an outline or “skeleton” of the page that users can navigate using hot keys, and which is similar to the visual hierarchy sighted users get from viewing your page. We This is why using appropriate heading levels, regardless of what the heading size looks like. (Note that while we can style headings to look differently where needed, visual consistency is just as important.)</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/rotor-headings.png" alt="Screenshot of macOS's VoiceOver rotor open on sarasoueidan.com" />
<figcaption>VoiceOver’s rotor menu exposes all headings available on a Web page, allowing the user to navigate the page more efficiently.</figcaption>
</figure>
<p>Semantics also convey purpose. HTML elements, as Jeremy Keith puts it in <a href="https://resilientwebdesign.com/">his Web book “Resilient Web Design”</a> <q>are a vocabulary of meaning</q>. When you use the proper HTML elements everywhere, it allows different apps and devices to convey your content’s meaning to the user so that they know what to expect of it and how to interact with it.</p>
<p>For example, when you use <code><button></code> to create a button, a screen reader exposes that button as what it is, and the user knows that they can do a specific action using that button (usually specified using the button’s accessible name) by either pressing the SPACE key or the ENTER key. The native <code><button></code> element comes with all the functionality and accessibility built into it by default.</p>
<p>But if you <em>don’t</em> use a <code><button></code> to create a button, and you choose to use a <code><div></code>, for example, instead, you lose all the built-in semantics and keyboard interactivity, making the button completely inaccessible to screen readers.</p>
<p>“But you can use ARIA attributes to turn a div into a button! Right?”</p>
<p>Well, yes and no…</p>
<h3 id="aria-is-a-polyfill-for-html-semantics." tabindex="-1">ARIA is a polyfill for HTML semantics.</h3>
<p>ARIA attributes are possibly the most powerful tool in our accessibility arsenal. Many ARIA attributes mirror native HTML semantics, while others provide semantics that do not natively exist in HTML. They don’t change behavior or add functionality. They don’t make an element focusable or add keyboard behavior, for example. So if you do choose to go the route of making a button out of a div, you’re gonna have to add that functionality yourself using JavaScript. But why create a brittle implementation of something that is already provided to you by default?</p>
<p>The <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of ARIA</a> states:</p>
<blockquote>
If you can use a native HTML element or attribute with the semantics and behavior you require <strong>already built in</strong>, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, <strong>then do so</strong>.
</blockquote>
<p>So while we should defer to using native HTML elements whenever possible, there are certain widgets that we can only build with the help of ARIA. For instance, there are no native HTML equivalents for Tab widgets (e.g. markup consisting of role=tab, tablist and tabpanel) — with all the interactivity built into them by default, so we can create tabs by repurposing other elements with ARIA and exposing them combined as a tabbed UI to screen reader users (e.g. role=“tab”, role=“tablist”, etc.)</p>
<p>Because ARIA fundamentally changes the exposed semantics and accessibility mappings of elements, if incorrectly used, it can have negative impacts on the way content is exposed to people using assistive technologies. <a href="https://scottohara.me/">Scott O’Hara</a>, referencing the ARIA Practicing Guidelines notes:</p>
<blockquote>
<p>ARIA practices first principlal states "A role is a promise".</p>
<p></p><p>When using ARIA, or even HTML for that matter, think about what you’re promising the people who use your interface or consume your documents.</p><p></p>
<p></p><p>Are you fulfilling the promises you are making?</p><p></p>
</blockquote>
<p>Before using ARIA attributes, think about what you’re promising your users. ARIA attributes directly affect how elements are exposed in the accessibility tree, and therefore how they are communicated to your users. Use them wisely and sparingly, and make sure that what you’ve built meets the expectations you’ve created for your users. If you need pointers as to how to use and <em>not</em> use them, <a href="https://www.w3.org/TR/using-aria/">the Using ARIA</a> document is a great place to start.</p>
<blockquote>
Top tip for accessibility from <a href="https://twitter.com/LeonieWatson">@LeonieWatson</a> - there are about 147 HTML elements and only two of them have no built in accessibility features - <code><div></code> and <code><span></code>. Use the right semantic elements as much as possible!
<cite>— <a href="https://twitter.com/edinbeth/status/1176161245352927237">Beth Fraser</a></cite>
</blockquote>
<blockquote>
<p>Simplest #a11y advice I've ever received: the div is your last resort. Is this your main content? There's a tag for that. Is it your header? That's got a tag, too. The div isn't bad; it just doesn't mean anything. That means you certainly don't need it for something clickable!</p>
<p>Assume your content has a specific meaning and try to find the tag for that meaning. If it doesn't, then the div (or span) is what you want! The div does have a place. That place is not what many tutorials would have you believe.</p>
<p><cite>— <a href="https://twitter.com/codeability/status/1162861059822112770">E.J. Mason</a></cite></p>
</blockquote>
<h3 id="javascript-is-imperative-for-creating-truly-accessible-custom-interactive-components." tabindex="-1">JavaScript is imperative for creating truly accessible custom interactive components.</h3>
<p>Even though you can get away with creating “functional” interactive components such as a CSS-only modal overlay or a disclosure widget using the infamous checkbox hack, it is <em>almost</em> always guaranteed that those CSS-only components are not truly accessible.</p>
<p>When you create an interactive widget, that widget is most likely to have a state. A disclosure widget is either “open” or “closed” (or “expanded/collapsed”). That state is exposed to screen reader users using ARIA attributes (e.g. <code>aria-expanded= "true/false</code>), and when a user interacts with the widget, the state changes, and that change needs to be conveyed to the user. JavaScript is <em>needed</em> to do that. I made peace with this fact two years ago when I <a href="https://sarasoueidan.com/blog/accessible-tooltips">needed to create an accessible tooltip</a> for one of my clients.</p>
<p>JavaScript is also needed to add keyboard functionality to custom components (e.g. tabs in a tabbed interface need to be nagivatable by arrow keys, and a disclosure widget needs to be operable using SPACE and ENTER keys).</p>
<p>Side note: Even though JavaScript is a requirement for creating interactive custom components, in order to make sure that your content is inclusive of all users regardless of their context, you can, and probably should — whenever possible — make sure that they can consume that content when JavaScript is not available. In fact, you should never assume that your users will have JavaScript, as there are <a href="https://kryogenix.org/code/browser/everyonehasjs.html">plenty of reasons</a> why they might not. This is why progressive enhancement is the most accessible strategy for building for the Web. I would even go as far as saying that it is a requirement, in some cases, for creating inclusive documents and components.</p>
<h3 id="progressive-enhancement-is-an-inclusive-strategy-for-building-for-the-web." tabindex="-1">Progressive enhancement is an inclusive strategy for building for the Web.</h3>
<p>There seems to be a misconception among many developers that just because JavaScript is required to make an interactive component accessible then you cannot possibly build it with progressive enhancement as an approach. The reason for that is that those developers think that Progressive Enhancement is anti-JavaScript. That is a fallacy.</p>
<p>Progressive enhancement is about layering — starting with the most resilient base — semantic HTML! — and then layering styles (CSS) and functionality (JavaScript!) on top as appropriate.</p>
<p>But what progressive enhancement achieves is having a resilient layer of content that will always be accessible, even when the CSS and/or JavaScript are absent.</p>
<p>From the first day that I learned about progressive enhancement, it’s become my go-to strategy for building Web interfaces. I can’t imagine creating for the Web another way. And when I started building with accessibility in mind, I was convinced that it is the most sensible approach to developing for the Web.</p>
<p>Let’s take a Tabs component again as an example. In a tabbed component, you have a series of tabs that control their respective content panels. The user can navigate between these panels by clicking on the tab they want. Tabs require JavaScript to be operable and accessible. But what if JavaScript does not work? If the Tabs were not progressively enhanced — if they were built with the assumption that JavaScript would always be there, then the component will fail to function and the content in all the hidden panels will be completely inaccessible.</p>
<p>But if think about Tabs from a progressive enhancement point of view, you’ll want to consider what the content inside of it would look like had it been only created using HTML — without the CSS that adds the Tabbed UI affordances, and without the JavaScript that makes it behave like typical tabs.</p>
<p>In this case, you may think of the tabs and panels as a series of sections, each with a title and content. These sections may or may not have a table of contents at the top. This would be the default “view”, and is what the user gets by default when the JavaScript doesn’t work.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/tabs-1.png" alt="Illustration of a series of sections enhanced to a tabbed interface." />
<figcaption>A tabs component may be enhanced from a series of sections, each with a title and some related content.</figcaption>
</figure>
<figure>
<img src="https://sarasoueidan.com/assets/images/tabs-2.png" alt="Illustration of a series of sections preceded by a table of contents enhanced to a tabbed interface." />
<figcaption>A tabs component may also be enhanced from a series of sections, each with a title and some related content, with a table of contents preceding these sections.</figcaption>
</figure>
<p>Then, when the JavaScript runs, you can enhance those sections by changing their layout and display, and adding the required interactivity. The way I would do it is I would probably add a style hook I want in the markup, and then I’d use that to change the layout of my sections knowing that the JavaScript has run and the Tabs will be operable and interactive.</p>
<p>When you <a href="https://www.deque.com/shift-left/">shift accessibility to the left</a> of your process, using progressive enhancement as a development strategy makes even more sense. Pretty much every single non-native interactive component I have built over the past year has been enhanced from a basic non-interactive component.
Start with HTMl first. Utilize everything it has to offer to provide an accessible basic experience. Then enhance with CSS and JavaScript when they are available.</p>
<blockquote>
<p>Progressive enhancement. Because sometimes your JavaScript just won’t work.</p>
<p>Be prepared.</p>
<p><cite>— <a href="http://www.kryogenix.org/">@sil</a></cite></p>
</blockquote>
<h3 id="design-does-not-always-dictate-implementation." tabindex="-1">Design does not always dictate implementation.</h3>
<p>One of the most common ways I see semantics broken is when they are derived from the visual design. Because the visual design does not always describe the <em>type</em> of content it is representing.</p>
<p>Headings are a perfect example here. In <a href="https://www.youtube.com/watch?v=are7ZZgA86I&list=PLe9psSNJBf74yYiVXDXz8UnRnWf3NHzS-&index=5">a recent talk I gave</a>, I showed an example from one of my latest client projects, where the page was designed in a way that a main heading for the page was seemingly non-existent, when in fact it wasn’t. It was just not styled like a large level one heading normally would.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/codrops-heading.png" alt="screenshot of the page demonstrating how the page's main heading is not visually styled like a main heading." />
<figcaption>
The main title of the page that describes the content in the page's main section is positioned and styled in a way that (undeliberately) disguises the fact that it is a main heading. Even though it is styled like a level 6 heading, it is implemented as a level 1 heading in the markup to represent what it really is.
</figcaption>
</figure>
<p>Thinking about the document structure and screen reader user expectations, I knew the page needed to have a main heading. While I had to provide a heading for screen reader users only for some other pages where one was visually absent, in this particular case the heading was already there, it was just styled to look different. So it <em>looked</em> like the page didn’t have a main heading, but in reality it did.</p>
<p>Looking at the page through the lens of accessibility and keeping the HTML layer — semantics and structure — in mind, changed the way I <em>saw</em> the page, and fundamentally changed how I ended up coding it.</p>
<p>So just because a component or element looks a certain way doesn’t mean that it is. And just because it doesn’t look a certain way, doesn’t mean that it isn’t.</p>
<p>The same is true for interactive UI patterns. The same pattern may create a different experience depending on the context it is in. And often times the context defines how a pattern should behave, what the user experience should be like, and that, in turn, determines what the underlying semantics should be, and that drives implementation.</p>
<p>In the same project I just mentioned, there was a seemingly simple UI pattern that turned out to be quite an interesting UX and accessibility challenge. The following image is a screenshot of that component:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/codrops-video-player.png" alt="Screenshot of the Video Player component." />
<figcaption>The featured videos component in my recent client project shows a video on the left with a playlist of videos on the right. Clicking on any of the video titles on the right would load the video on the left; but it would not autoplay it. (Note that the content in this screenshot is dummy.)</figcaption>
</figure>
<p>In order to implement this video player, I needed to know how it worked, so I can mark it up in a way that conveys its semantics and functionality to screen readers properly.</p>
<p>Even though the list of video titles on the right looks like a list of links, it is not <em>really</em> a list of <em>links</em>, because a link is supposed to take you somewhere, but these “links” don’t. Clicking on a video title would load that title in the player on the left. So it is an interactive element that performs an action and that is not a link. Therefore, and even though their appearance doesn’t show it, these titles are actually <em>buttons</em>.</p>
<p>Then there was the question of what happens when a title is clicked? does the video autoplay? If it does, then the button should probably also pause the video, making it a type of a toggle button. But if you play/pause a video from the title button, you’d want to associate that button to the play/pause buttons inside the video itself, which can be a challenge given that the video could be a Youtube video, or a Vimeo video, or self-hosted. And if you don’t autoplay the video, should you move focus to the iframe after the button is clicked?</p>
<p>After reviewing the intended UX and testing with screen readers, I ended up implementing it as a tabs component, with all tabs controlling the one panel containing the video.</p>
<p>I had never thought about a tabbed interface with multiple tabs controlling the same one panel before. But the context of this component and the UX of it triggered a train of thought and UX considerations that drove the final implementation.</p>
<p>So one of the things that I learned from this component was that the UX drives the implementation. ARIA comes with a lot of attributes that enable us to create different UI patterns in different contexts, and sometimes all we need to do is modify the pattern a little bit to work in the context it is in.</p>
<blockquote>
<p>It’s weird that we still derive the semantics from a visual design instead of the other way around.</p>
<p>The visual design is different per context while the core semantics are not.</p>
<p><cite>— <a href="https://twitter.com/rikschennink/status/1166771758684356608">Rik Schennink</a></cite></p>
</blockquote>
<h3 id="just-because-it-is-technically-accessible%2C-doesn%E2%80%99t-mean-it-is-inclusive." tabindex="-1">Just because it is technically accessible, doesn’t mean it is inclusive.</h3>
<p>You can build something that is technically accessible but isn’t inclusive. That element or component may have all the buttons you want and you may be able to navigate it using a keyboard and use it with a screen reader, but did you really take your user’s needs and expectations into consideration when you made the decisions about how and what of that element should be exposed and interacted with?</p>
<p>In his talk “<a href="http://feather.ca/inclusion/aea2019">Inclusive by Design</a>”, Derek Featherstone, accessibility advocate and designer, talks about how he and his team built an accessible video player for an organization that they were contracted by. The video player had lots of buttons and was tested for technical accessibility over many phases.</p>
<p>But then when the time came and the component needed to be used by users with different disabilities, they realized that, even though they had built the perfect accessible video player, that video player was not truly inclusive — it was missing certain functionality that made using the player easier for a group of users, such as a Rewind or Fast Forward button. Derek and his team had also made assumptions about how all users would be using the video player, and forgot about the users that were using an older version of a screen reader and who were therefore missing important announcements that were supposed to help them operate the video player. So after several iterations and tests with various users, they ended up adding features to the video player that would take many more disabilities into account, and expectations that those users would have of the player, and that made it far more inclusive and tremendously improved its user experience.</p>
<p>Derek’s talk is full of such good examples that emphasize the importance of <strong>including your users early in the design process</strong>, and making sure that you embrace diversity by default. The idea is that if you’re designing something for me, I should have a reasonably meaningful way of participating in that thing. I should be represented in that somehow.</p>
<blockquote>
“Nothing about us, without us.”
<p><cite>- Michael Masutha</cite></p>
</blockquote>
<h4 id="at-the-end-of-the-day%2C-it-is-always-about-the-user." tabindex="-1">At the end of the day, it is <em>always</em> about the user.</h4>
<p>As you develop with real user experiences and inclusivity in mind, you’ll soon realize that a design pattern can be built in more than just one way. And there are <em>quite</em> a few things in accessible design that are opinionated.</p>
<p>Modal overlays are a great example. Regardless of being an annoying UI pattern, there are quite a few discussions around how they should be implemented and how they should behave once they are opened: Should you focus the first focusable element inside the modal? What if there is no focusable element? What if the first focusable element is the close button? Would you want the modal overlay to prompt a closing action as soon as it’s opened? (Of course not.) What if the first focusable element is an input field asking the user for their email address? Is it appropriate to prompt the user for their personal information without context first? (Also of course not.)</p>
<p>At the end of the day, no matter what decision you end up making, it should always be about the user. So getting the user and/or a more diverse team involved in the design and development process is crucial in building truly inclusive Web interfaces.</p>
<p>But what if you can’t? What if you don’t have access to such an environment or team? What if, like me, you’re a solo developer who often joins teams that usually don’t have users or disabled people involved?</p>
<h3 id="when-in-doubt%2C-ask-for-help." tabindex="-1">When in doubt, ask for help.</h3>
<p>If you can’t do it yourself, you can seek the experience and advice of other people who can. And it is very important for you to be open to constructive feedback.</p>
<p>There are quite a bunch of wonderful (and sometimes understandably grumpy) accessibility experts in our field who <em>want</em> the Web to be and become more accessible. Most of them have been doing free work like giving free advice and sharing their valuable knowledge in various forms — writing articles and books, making video courses, giving talks and workshops, etc. And there is a lot that we can learn from them. And most of them welcome questions and inquiries (paid and free).</p>
<p>Some of my go-to experts are (in no particular order):</p>
<ul>
<li><a href="https://tink.uk/">Leonie Watson</a></li>
<li><a href="http://feather.ca/">Derek Featherstone</a></li>
<li><a href="http://scottohara.me/">Scott O’Hara</a></li>
<li><a href="https://marcysutton.com/">Marcy Sutton</a></li>
<li><a href="https://www.youtube.com/channel/UCJAtIv92EJqzG2EOzo92sdQ">Rob Dodson</a></li>
<li><a href="https://twitter.com/sundress">Alice Boxhall</a></li>
<li><a href="https://marcozehe.de/">Marco Zehe</a></li>
<li><a href="https://ericwbailey.design/">Eric Bailey</a></li>
<li><a href="https://twitter.com/stevefaulkner">Steve Faulkner</a></li>
<li><a href="https://www.paciellogroup.com/">The Paciello Group</a></li>
</ul>
<p>You’d do well to spend some time reading the ARIA specifications and guidelines, and trying to get acquainted with as much about accessibility as you can, before asking for help. After all, these kind people may be able to help us, but they shouldn’t have to do our work for us — at least not for free.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>Accessibility isn’t easy. And often times it is downright hard. But that comes with the territory. Designing for humans is hard. And accessibility is, at the end of the day, all about and all <em>for</em> humans.</p>
<p>We may not get it completely right, and there may always be room for improvement — especially as more users use our products and consume our content, but one thing I know is that that should never discourage us. Almost everything can be improved in one way or another. The most important thing is to be open to feedback and to be empathetic enough to <em>care</em> about our users and try our best to make their lives easier with the (powerful) tools we have at our disposal.</p>
<p>Use HTML. Enhance with CSS. Leverage the power of JavaScript. And include your users in your design process as early as possible. And always look at your work through the lens of inclusion. This will take you far ahead. Then learn more, iterate, and improve. And don’t forget, we’re all <a href="https://uxmag.com/articles/we-re-just-temporarily-abled">just temporarily abled</a>. --></p>
Case Study: Implementing Accessible Data Charts for the Khan Academy 2018 Annual Report
2019-10-10T09:54:41Z
https://sarasoueidan.com/blog/accessible-data-charts-for-khan-academy-2018-annual-report/
<p class="deck">
A few months ago I teamed up with <a href="https://superfriend.ly/">SuperFriendly</a> to <a href="https://sarasoueidan.com/work/khan-academy-2018-annual-report">create an accessible micro-site</a> for the <a href="https://khanacademyannualreport.org/">Khan Academy 2018 Annual Report</a>. The site is a very beautiful visual representation of Khan’s real-life impact on world education, their end-of-year financial reports, and more. By nature, the annual report contains a <em>lot</em> of data and visualizations and charts to represent it. My mission was to make sure that the way this data is presented and implemented is as accessible to site’s visitors as possible, regardless of how they explore the site.
</p>
<p>
Data visualizations are a great way to make information stand out and sometimes a lot easier to parse and understand. But they also come with their own accessibility challenges that you need to be aware of.
</p>
<figure role="figure" aria-label="The Khan Academy annual report represented all of the main data showcasing the impact they had on world education using different kinds of charts — simple and complex." class="wide">
<img src="https://sarasoueidan.com/assets/images/work/khan-academy/khan-academy-chart.png" alt="An example of a chart from the Khan Academy 2018 Annual Report. Screenshot." />
<figcaption>
<a href="https://khanacademyannualreport.org/">The Khan Academy annual report</a> represented all of the main data showcasing the impact they had on world education using different kinds of charts — simple and complex.
</figcaption>
</figure>
<p>When it comes to visualizing data on the Web, SVG is the most used format. It is perfect for creating data visualizations because it comes with all the elements needed to create the shapes used to represent information. Because SVG is an XML format, similar to HTML, it comes with a lot of tags to represent different kinds of elements, mostly shapes. We have <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Basic_Shapes">elements for creating basic shapes</a> such as <code>circle</code> s, <code>ellipse</code> s, <code>rect</code> angles, <code>polygon</code> s, <code>polyline</code> s, <code>line</code> s, and more complex <code>path</code> s, all of which are used to draw all kinds of data charts and visualizations.</p>
<p>And often times, these charts and visualizations are also animated and interactive; and SVG provides us with the ability to animate and interact with the images we create with it — using CSS and JavaScript.</p>
<p>But SVG data visualizations are, at the end of the day, SVG <em>images</em>. This means that when a screen reader comes across one, it is likely going to announce it the same way it would announce any image: as an image, with an accessible name (because I’m assuming that the image <em>does</em> have an accessible name provided in the <code>alt</code> attribute). This, in turn, means that the screen reader is not going to announce the content presented <em>inside</em> the chart to the user by default. It is only going to announce whatever accessible name we provide it with. Making sure it announces the content inside the image is something that requires a little more work on our side.</p>
<h3 id="how-accessible-are-svg-data-visualizations-%26-charts%3F" tabindex="-1">How accessible <em>are</em> SVG data visualizations & charts?</h3>
<p>Albeit being a perfect candidate for <em>visually</em> representing information, SVG is not equipped with the semantics needed to convey <em>the content</em> of that information to the user.</p>
<p>In <a href="https://tink.uk/accessible-svg-line-graphs/">her article about creating accessible line graphs with SVG</a>, Léonie Watson says:</p>
<blockquote>
<p>“SVG is often used for data visualisation, but because SVG lacks the semantics to express structures like bar charts, line graphs, and scatter plots, the content is difficult for screen reader users to interpret.”</p>
</blockquote>
<p>Léonie wrote a few articles about making content inside SVG accessible that I will link to at the bottom of this article. The concept in all of them is the same: use some of the available ARIA roles to modify the semantics of SVG elements to be announced to screen readers as something other than what they are.</p>
<blockquote>
SVG has no native semantics for representing structures like tables, but ARIA1.1 introduces a number of roles that can be used to polyfill the necessary semantic information.
<cite>— Léonie Watson, <a href="https://tink.uk/accessible-svg-tables/">Accessible SVG Tables</a></cite>
</blockquote>
<p>For example, in her article, she uses the ARIA table attributes to announce a visual line graph as a table to screen reader users, which makes it possible for them to announce the data inside those tables to their users as well. This is possible because a table is often times an appropriate alternative way to represent the information in a line graph.</p>
<p>But Léonie finally notes (and emphasis is mine here) that:</p>
<blockquote>
<p>“The state of SVG accessibility support in browsers and screen readers is still highly inconsistent, even when ARIA is used to polyfill semantic information.</p>
<p>The upshot is that this technique is enough to make the primary SVG content more screen reader accessible […] but it isn’t enough to make it completely so. For this (and many other good reasons), <strong>it’s a good idea to provide an alternative view of the information</strong>, for example by providing both a graphical and a tabular view of the content”</p>
</blockquote>
<p>Personally, I’d avoid hacking my way around making an SVG chart accessible using ARIA roles. Most of the charts in the Khan Academy annual report were more complex, so using ARIA roles to change their semantics would not only prove to be counter-productive, but also risky, especially given how inconsistent screen reader and browser support is, and how important those charts are for the site’s visitors.</p>
<p>So, I chose the alternative view of the information route. And with all the different SVG embedding techniques available to use, SVG makes all kinds of fallbacks easy to implement.</p>
<h3 id="choosing-the-most-appropriate-svg-embedding-technique" tabindex="-1">Choosing the most appropriate SVG embedding technique</h3>
<p>There are 6 ways to embed an SVG on a Web page. Choosing the most appropriate one highly depends on your requirements and what you need the SVG to be capable of <em>and</em> the kind of fallback that you need.</p>
<ul>
<li>For data visualizations and infographics, it’s usually best to provide the plain text (or table) version of the content of the image as fallback. Since that text fallback is likely to be more than one or two sentences long, we need to embed the SVG using an embedding technique that caters for that.</li>
<li>We were also planning on animating the charts had the project timeframe allowed it. This means that the embedding technique also needs to allow that.</li>
<li><em>And</em> I know that we will need JavaScript for the animation, which also narrows down my embedding options further.</li>
</ul>
<p><code><iframe></code> , <code><object></code> and inline <code><svg></code> all tick all of the above boxes.</p>
<p>I’m not a big fan of <code><iframe></code> s, and <code><object></code> already gives me everything I need that <code><iframe></code> does, so, out of these two, I always choose <code><object></code> .</p>
<p>In addition to the above requirements, I had a couple of other considerations in mind:</p>
<ul>
<li>we have a <em>lot</em> of charts, especially on the <a href="https://khanacademyannualreport.org/leveling-the-playing-field/#introduction">leveling the playing field</a> page,</li>
<li>and some of those charts are quite larger than the others. So I’ll want to keep page load and performance in mind.</li>
</ul>
<p>If I were to embed the charts inline, they would “pollute” my HTML, and would not be cached unless the whole page is. That’s not ideal. Whereas an <code><object></code> would reference an external SVG image which, after the first request, would then be cached, making subsequent visits faster.</p>
<p>Also, on a more design-related note: I was working with SVG images exported from Adobe Illustrator, which tends to repeatedly create and use the same IDs and CSS class names for the images it exports. If I were to embed all of the images inline, the IDs and class names would clash and I would need to spend time I didn’t have to go over each SVG and provide it with unique class names to avoid the style inheritance nightmare, because, by default:</p>
<blockquote>
<p>Styles within an inline <code><svg></code> are not scoped to it.</p>
</blockquote>
<p>This means that if you have two inline <code>svg</code> s on the page, the styles from the second SVG in the DOM order will override the styles in the first one for elements with shared class names.</p>
<p>And last but not least, I love the clean and uncluttered way that <code><object></code> enables me to provide text fallback for my SVG images: simply place the text fallback in between the opening and closing <code>object</code> tags, and that text will be displayed wherever SVG is not supported and/or whenever the SVG fails to load.</p>
<p>So, <code><object></code> it is.</p>
<h3 id="embedding-the-charts" tabindex="-1">Embedding the charts</h3>
<p>The initial base code for one of the simple charts would then look like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>object</span><br /><span class="highlight-line"></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span> <span class="token attr-name">data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/images/long_beach.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Khan Academy math usage for 30 minutes per week proved to </span><br /><span class="highlight-line"> improve more than 5,000 Long Beach students scores by 2 times </span><br /><span class="highlight-line"> in the Smarter Balanced Assessment.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>object</span><span class="token punctuation">></span></span> </span></code></pre>
<p>This was just the base markup, but I was curious enough to check how it would be announced by a screen reader. I’m on macOS so I usually test my work with VoiceOver (VO) on Safari.</p>
<p>VO announced the chart as a frame, and used the file name of the SVG (in this case <code>long_beach.svg</code> ) as the accessible name for it. Ouch.</p>
<p>I was expecting the random name situation since my markup doesn’t yet include a label for the image, but I didn’t know how an <code>object</code> is usually announced. I was also curious if VO would <em>somehow</em> recognize or find the fallback text and announce it. It didn’t. Now, it was time to start “fixing” the situation.</p>
<p>I want my chart to be announced as an image to start, so I needed to modify its semantics using ARIA <code>role</code> . To give it an accessible name, I used <code>aria-label</code> :</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>object</span><br /><span class="highlight-line"> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img<span class="token punctuation">"</span></span> </span><br /><span class="highlight-line"> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“Khan</span> <span class="token attr-name">Academy</span> <span class="token attr-name">Math</span> <span class="token attr-name">usage</span> <span class="token attr-name">level</span> <span class="token attr-name">in</span> <span class="token attr-name">Long</span> <span class="token attr-name">Beach.</span> <span class="token attr-name">Chart."</span></span><br /><span class="highlight-line"></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span> <span class="token attr-name">data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/images/long_beach.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Khan Academy math usage for 30 minutes per week proved to </span><br /><span class="highlight-line"> improve more than 5,000 Long Beach students scores by 2 times </span><br /><span class="highlight-line"> in the Smarter Balanced Assessment.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>object</span><span class="token punctuation">></span></span> </span></code></pre>
<p>With these two attributes added, VO communicated my chart as an image, and used the label I gave it as its accessible name. Great.</p>
<p>But a visitor using VO would now know that there is a chart showing Math usage levels in Long Beach, but they would not be able to access the data inside that chart.</p>
<p>Since I already have a text fallback clearly describing the data in the chart right there in my markup, I thought I could use that instead of try to communicate the same content in a second way. That text fallback was not being exposed to my screen reader. In other words, it was <em>hidden</em> from screen readers.</p>
<p>But I already know that I can expose hidden content to my screen reader to announce if I reference that content inside of <code>aria-labelledby</code> . I’ve used this in <a href="https://sarasoueidan.com/blog/accessible-icon-buttons">my previous article</a> to provide a visually-hidden accessible label to an icon-only button. So it makes sense that I’d be able to do the same using <code>aria-describedby</code> .</p>
<p>There are several reasons why I’d want the image’s text content exposed using <code>aria-describedby</code>
not <code>aria-labelledby</code> :</p>
<ul>
<li><code>aria-labelledby</code> is used to provide an accessible label to an element. My image already has one that I have provided in <code>aria-label</code> .</li>
<li>The text content is used to <em>describe</em> the (content of) the image.</li>
<li>An accessible name (label) should be short and succinct. A description is usually longer, especially (and particularly) in the case of text (or table!) provided for charts and visualizations.</li>
<li>And, last but not least, I want the text content to be announced after the image and its accessible name are. That’s exactly what <code>aria-describedby</code> does.</li>
</ul>
<blockquote>
There is a major difference between the way the content referenced in <code>aria-labelledby</code> is announced versus that referenced in <code>aria-describedby</code>. The accessible name is announced <em>before</em> the image’s role is announced (in VO, at least); the description is announced <em>after</em> the image’s role, <strong>with a short pause preceding it</strong>.
</blockquote>
<p>That’s exactly how I want my text to be announced. So, with all that taken into account, the final code for my chart looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">< object</span><br /><span class="highlight-line"> role="img" </span><br /><span class="highlight-line"> aria-label=“Khan Academy Math usage level in Long Beach. Chart."</span><br /><span class="highlight-line"> aria-describedby="long_beach_desc"</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> type="image/svg+xml" data="/images/long_beach.svg"></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>long_beach_desc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Khan Academy math usage for 30 minutes per week proved to </span><br /><span class="highlight-line"> improve more than 5,000 Long Beach students scores by 2 times </span><br /><span class="highlight-line"> in the Smarter Balanced Assessment.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">< /object> </span></code></pre>
<p>This works very well, including for images that have longer descriptions. Of course, we made sure (thanks, Jessi) that the descriptions were as concise and succinct as possible, so the user is not overwhelmed with long announcements but would still get a very clear and accurate text version of the information provided in each chart.</p>
<h3 id="%E2%80%9Ccan%E2%80%99t-you-just-use-%3Cfigure%3E-for-the-charts%3F%E2%80%9D" tabindex="-1">“Can’t you just use <code><figure></code> for the charts?”</h3>
<p>I got this question a couple of times after speaking about my implementation choice above — once during a workshop, and once after a talk.</p>
<p>I’m guessing that the idea here is that you would implement the SVG chart in an <code>img</code> inside a <code>figure</code> and use the <code>figure</code> ’s <code>figcaption</code> to provide the text alternative of the chart’s content to screen reader users. I assume the suggestion would be to hide the figcaption in this case visually (because we don’t show it visually, of course) and make it available to screen readers only, using a utility class such as <code>sr-only</code> .</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">< !-- This is what you should NOT do.--> </span><br /><span class="highlight-line">< figure> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/path/to/chart.svg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ACCESSIBLE_CHART_TITLE<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figcaption</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Here goes the text that describes the information inside the chart.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Note that this text could be a couple of a few paragprahs long.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>It might even be appropriate to represent the data using a table!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">< /figure> </span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token comment">/* <br /><span class="highlight-line"> * Utility class to hide content visually while keeping it screen reader-accessible.</span><br /><span class="highlight-line"> * Source: https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html </span><br /> */</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.sr-only:not(:focus):not(:active)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This approach has more than one issue.</p>
<ul>
<li>
<p>I didn’t choose <code><figure></code> because I excluded all SVG embedding options that don’t allow the SVG charts to be animated. <code><figure></code> is static — meaning that it won’t allow the would-be SVG <code>img</code> inside of it to be animated.</p>
</li>
<li>
<p>I also didn’t consider <code><figure></code> as an option because a <code>figure</code> is, by definition, meant to be a meaningful wrapper for a piece of content that is relevant to the current document, but not vital to its understanding.</p>
</li>
</ul>
<blockquote>
<p>[The <code>figure</code> element] can thus be used to annotate illustrations, diagrams, photos, code listings, etc, that are referred to from the main content of the document, but that could, without affecting the flow of the document, be moved away from that primary content, ... </p>
<cite>— <a href="https://www.w3.org/TR/2011/WD-html5-author-20110809/the-figure-element.html">HTML5: Edition for Web Authors</a></cite>
</blockquote>
<p>Khan Academy’s annual report charts <em>are</em> vital to the understanding of the page they’re in, and <em>cannot</em> be removed from the primary document without affecting it. The data inside these charts <em>is</em> the meat of the annual report.</p>
<p>So, as far as semantics are concerned, <code>figure</code> is not an element suitable to represent these charts.</p>
<ul>
<li>The <code>figure</code> ’s <code>figcaption</code> is used by screen readers as <strong>the accessible name</strong> of the _ <code>figure</code> _. As I mentioned earlier, the text fallback for the charts is <em>not</em> meant to be used as a name (label), and it should be announced as a <em>description</em>, not a name.</li>
</ul>
<p>Furthermore, the markup for the figure would have to be very dirty even if using it <em>were</em> possible, because the text that goes in the <code>figcaption</code> needs to be provided inside an <code>aria-label</code> on the <code>figure</code> alongside a <code>role</code> attribute for certain browser-screen-reader combinations to even announce <code>figure</code> as it is intended to be announced; and this brings us again to the “but the text inside <code>figcaption</code> is not even a label for the <code>figure</code> element” argument…</p>
<p>My friend <a href="https://twitter.com/scottohara">Scott O’Hara</a> has <a href="https://www.scottohara.me/blog/2019/01/21/how-do-you-figure.html">a wonderful article</a> about <code>figure</code> and <code>figcaption</code> detailing how to use and <em>not</em> use them, and how to ensure the best cross-browser and screen reader compatibility. I highly recommend checking it out.</p>
<p>When it comes to accessibility, semantics matter <em>a lot</em>. Learning about an element’s semantics and proper usage helps us make better informed decisions as to which element to use when.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>I had great fun working on the Khan Academy annual report, and I learned quite a bit in the process. Khan Academy’s mission is to provide free education for everyone everywhere. In other words, they are working to make world-class education <em>accessible</em> by everyone. The least I could do is to make sure the product I helped build for them was just as accessible by everyone, regardless of their context and abilities. I’m quite proud of the work we did. And if you could use my help building better, more accessible products, <a href="https://sarasoueidan.com/contact/">get in touch</a>.</p>
<p>And if you want to be the first to know when the next article comes out (hint: there will be more like this one), you can <a href="http://sarasoueidan.com/blog/index.xml">subscribe to my blog’s RSS feed</a>.</p>
<p>Thank you for reading.</p>
<h3 id="further-reading" tabindex="-1">Further Reading</h3>
<ul>
<li><a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">Inclusively Hidden</a></li>
<li><a href="https://www.scottohara.me/blog/2019/01/21/how-do-you-figure.html">How do you figure?</a></li>
<li><a href="https://tink.uk/accessible-svg-tables/">Accessible SVG Tables</a></li>
<li><a href="https://tink.uk/accessible-svg-line-graphs/">Accessible SVG Line Graphs</a></li>
<li><a href="https://tink.uk/accessible-svg-flowcharts/">Accessible SVG Flowcharts</a></li>
</ul>
Accessible Icon Buttons
2019-05-22T12:59:17Z
https://sarasoueidan.com/blog/accessible-icon-buttons/
<p class="deck">
An icon button is an icon that triggers some sort of action on the page. More accurately, technically speaking, an icon button is a <em>button</em> that contains an icon and no (visible) accompanying text. These buttons can be found in the majority of app and user interfaces today. The infamous hamburger menu button is a great example of such buttons when not visually labelled “Menu”.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/twitter-icon-buttons.png" alt="Twitter icon buttons" />
<figcaption>Twitter’s Compose Tweet icons are an example of icon-only buttons in the wild.</figcaption></figure>
<p>Putting aside the UX side of the coin and whether or not an icon alone is enough to convey meaning and functionality to users, many implementations of these buttons today lack the proper accessibility that makes them meaningful to users of assistive technologies.</p>
<p>While the seemingly popular <code>aria-label</code> is a perfectly valid way to add an accessible name to a button (and/or other components), it is certainly not the only way, let alone the best. You could always <a href="https://twitter.com/SaraSoueidan/status/1124974330864181253">just put text in it</a>, for example. But what if the designer or the UI enforces the absence of visual text next to an icon?</p>
<p>There is a handful of ways that an icon button can be implemented accessibly. This article is an overview of them all.</p>
<h3 id="accessible-button-names" tabindex="-1">Accessible Button Names</h3>
<p>A button is announced to screen readers as a button when the proper element — the almighty <code><button></code> is used, or when an element has a <code>role = "button"</code> on it. Either way, when a button is announced as one, users expect to be able to interact with it in a certain way, which includes being able to activate the button using either the <kbd>ENTER</kbd> or <kbd>SPACE</kbd> keys.</p>
<p>This means that if you choose not to use the native <code><button></code> element, you’ll need to reimplement that native behavior and functionality yourself using JavaScript. But who would want to create a brittle implementation of features that are provided to them by default by the browser when they can save both time and effort using a simple <code><button></code> , right? ; )</p>
<style>
.visually-hidden:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(100%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
button.icon-button {
background: #fff;
color: inherit;
border: 1px solid #000;
border-radius: 0.3em;
padding: 0.5em;
transition: all 0.2s linear;
display: flex;
align-items: center;
}
button.icon-button svg {
display: inline-block;
width: 1em;
height: 1em;
fill: #000;
transition: all 0.2s linear;
}
button.icon-button:focus {
outline: 2px solid #3bc9ff;
}
button.icon-button:hover {
background: #000;
color: #fff;
}
button.icon-button:hover svg {
fill: #fff;
}
</style>
<h4 id="inspecting-the-button-name-in-chrome-devtools" tabindex="-1">Inspecting the Button Name in Chrome Devtools</h4>
<p>To inspect what the accessible name of a button is and, therefore, how it will be announced by a screen reader, you can use the Chrome devtools. Next to the Style tab, you’ll find an “Accessibility“ tab, inside which you will find something that looks like this:</p>
<img src="https://sarasoueidan.com/assets/images/accessible-name-devtools.png" alt="The accessible button name as shown in the accessibility chrome devtools tab" />
<p>The accessible name for the button can be defined in several ways, as you can see in the image above. When you put plain text inside the button, that text content can then be used as a name for the button, which is then announced: <q>Send Message, Button</q> by VoiceOver.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/vo-button-announcement.png" alt="What the button is announced like in VoiceOver on Mac" />
<figcaption>The popup shows what VoiceOver on Mac announces a button that has “Send Message” text in it.<strong>The button is announced by its name first and then its role (button).</strong></figcaption></figure>
<p>You can inspect the button below yourself and check the accessible name in the devtools, and fire up your screen reader of choice to see how it is announced.</p>
<p><button>Send Message</button></p>
<p>Now, what happens when we have an icon inside the button?</p>
<h3 id="icon-sitting-next-to-text" tabindex="-1">Icon Sitting Next to Text</h3>
<p>This post is about icon-only buttons. But it’s worth starting with buttons that contain an icon sitting next to some text.</p>
<p>When a button contains text, that text is going to be used to create an accessible name for the button. That text should usually be enough to convey what the button does to screen readers.</p>
<p>As such, the icon itself (an <code>svg</code> image) could become irrelevant to screen reader users, as it does not clarify or add to the accessible name of the button. As such, the common practice is to hide it from screen readers using <code>aria-hidden</code> :</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-trigger<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 32 32<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32px<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content here --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> Menu</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>aria-hidden</code> attribute is used to indicate whether an element is exposed to screen readers or not. When set to “true”, the element and its contents are hidden from the accessibility API, regardless of whether or not it is visually displayed.</p>
<p>The <code>svg</code> also has a <code>focusable</code> attribute set to <code>false</code> which prevents the icon itself from receiving focus in IE, because otherwise the button will have two Tab stops, which is not the expected or desired behavior.</p>
<h5 id="further-reading%3A" tabindex="-1">Further Reading:</h5>
<ul>
<li><a href="https://developer.paciellogroup.com/blog/2012/05/html5-accessibility-chops-hidden-and-aria-hidden/">HTML5 Accessibility Chops: hidden and aria-hidden</a></li>
<li><a href="https://www.accessibility-developer-guide.com/examples/sensible-aria-usage/hidden/">Hiding elements from screen readers using aria-hidden</a></li>
<li><a href="https://www.scottohara.me/blog/2018/05/05/hidden-vs-none.html">Know your ARIA: ‘Hidden’ vs ‘None’</a></li>
</ul>
<hr />
<p>Now, if the button does not have any visible text, you’ll still need to provide an accessible name for it somehow. There are a few different ways to do that.</p>
<h3 id="technique-%231%3A-accessible-visually-hidden-text" tabindex="-1">Technique #1: Accessible Visually Hidden Text</h3>
<p>If your button isn’t supposed to visually contain any text, you can still provide the text inside the <code><button></code> so that it’s picked up and used by screen readers, while hiding it visually using CSS.</p>
<p>There are a few ways to hide content visually. You’ll want to make sure you hide the text but also keep it accessible to screen readers. My friend Scott O’Hara has written <a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">a fantastic article</a> that covers how to hide content accessibly that is definitely worth bookmarking and checking out to learn more about the topic.</p>
<p>Our icon button markup might now look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 32 32<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32px<span class="token punctuation">"</span></span> <br /><span class="highlight-line"></span><br /> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Menu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><em>Note that we’ll still want to always prevent the <code>svg</code> from receiving focus in IE using the <code>focusable</code> attribute.</em></p>
<p>Using a utility CSS class that I usually call <code>visually-hidden</code> <small>(short for “screen reader only”)</small>, we hide the text visually while making sure it can still be picked up and used by screen readers:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* <br /><span class="highlight-line"> * Utility class to hide content visually while keeping it screen reader-accessible.</span><br /><span class="highlight-line"> * Source: https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html </span><br /> */</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.visually-hidden:not(:focus):not(:active)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I borrowed this class from Scott’s article:</p>
<blockquote>
<p>The above “sr-only” class is utilizing various declarations to shrink an element into a 1px square, hiding any overflow, and absolutely positioning the element to remove any trace of it from the normal document flow.</p>
<p>The <code>:not</code> portions of the selector are allowing a means for any focusable element to become visible when focused/active by a user. So elements that normally can’t receive focus, like paragraphs, will not become visible if a user navigates through content via screen reader controls or the <kbd>Tab</kbd> key, but natively focusable elements, or elements with a non-negative <code>tabindex</code> will have these elements appear in the DOM on focus.</p>
</blockquote>
<p>VoiceOver will announce <q>Menu, Button</q>, and the devtools will confirm that the text inside the button is used to provide an accessible name to it:</p>
<img src="https://sarasoueidan.com/assets/images/visually-hidden-text-button.png" alt="" />
<p>You can try it yourself by inspecting the following button:</p>
<button class="icon-button">
<svg width="24px" height="24px" viewBox="0 0 24 24" focusable="false">
<path d="M21 11h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
<path d="M3 7h18c0.6 0 1-0.4 1-1s-0.4-1-1-1h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1z"></path>
<path d="M21 17h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
</svg>
<span class="visually-hidden">Menu</span>
</button>
<h5 id="further-reading%3A-1" tabindex="-1">Further Reading:</h5>
<ul>
<li><a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">Inclusively Hidden</a></li>
<li><a href="https://www.w3.org/TR/WCAG20-TECHS/ARIA14.html">ARIA14: Using aria-label to provide an invisible label where a visible label cannot be used</a></li>
</ul>
<h3 id="technique-%232%3A-accessible-visually-hidden-text-with-hidden-and-aria-labelledby" tabindex="-1">Technique #2: Accessible Visually Hidden Text with <code>hidden</code> and <code>aria-labelledby</code></h3>
<p>This technique is similar to Technique #1 with one advantage over it: you don’t need to use a CSS utility class to hide the text visually. This technique uses the <code>hidden</code> attribute to hide the button text:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-trigger<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button-label<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>Menu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 28<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>hidden</code> attribute is a boolean attribute. When specified on an element, it hides that element both visually and from assistive tech. It is the HTML5 markup equivalent of the CSS <code>display: none</code> and <code>visibility: hidden</code> declarations, both of which also hide elements inaccessibly.</p>
<p>That said, the ARIA specification allows the <code>aria-describedby</code> and <code>aria-labelledby</code> attributes to reference hidden elements. This means that those hidden elements can and will then be discoverable and used by screen readers. Great!</p>
<p>Using <code>aria-labelledby</code> , we can use our hidden piece of text as a label for our button.</p>
<p>The <code>aria-labelledby</code> attribute establishes relationships between an element and its label(s). It takes one (or more) ID as a value, which refers to the element that will be used as a label to the element it is used <em>on</em>. This idea is similar to how the <code>for</code> attribute is used to link a <code><label></code> to an <code><input></code> .</p>
<p>Using this markup, VoiceOver will announce <q>Menu, Button</q>, and the devtools will now indicate that the accessible name of the button was provided by a <code><span></code> containing a piece of text that is referenced via an <code>aria-labelled</code> attribute:</p>
<img src="https://sarasoueidan.com/assets/images/hidden-text-button.png" alt="" />
<p>You can try it yourself by inspecting the following button:</p>
<button class="icon-button" aria-labelledby="button-label">
<span id="button-label" hidden="">Menu</span>
<svg width="24px" height="24px" viewBox="0 0 24 24" focusable="false">
<path d="M21 11h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
<path d="M3 7h18c0.6 0 1-0.4 1-1s-0.4-1-1-1h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1z"></path>
<path d="M21 17h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
</svg>
</button>
<p>One final note: the <code>hidden</code> attribute has very, very good cross-broswer support. But if you still need to support older IEs (I am so, so sorry!), you’ll want to “polyfill” it in the CSS by explicitly hiding any and all elements that have a <code>hidden</code> attribute set on them:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">[hidden]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h5 id="further-reading%3A-2" tabindex="-1">Further Reading:</h5>
<ul>
<li><a href="https://developer.paciellogroup.com/blog/2012/05/html5-accessibility-chops-hidden-and-aria-hidden/">HTML5 Accessibility Chops: hidden and aria-hidden</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute">Using the aria-labelledby attribute</a></li>
</ul>
<h3 id="technique-%233%3A-using-aria-label" tabindex="-1">Technique #3: Using <code>aria-label</code></h3>
<p><code>aria-label</code> is another attribute which can be used to provide an accessible label to an element. Unlike <code>aria-labelledby</code> which references another element to use as a label, <code>aria-label</code> takes a string of text as a value, and that string will be used as the accessible name for the element it is used on.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-trigger<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 28<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>When <code>aria-label</code> is used on a button, the contents of the attribute will override the contents inside the button as the accessible name. This means that, if you have an icon or even other text content inside your <code><button></code> , that content will no longer be announced as part of the button’s name. VoiceOver will announce <q>Menu, Button</q>, and the devtools will now indicate that the accessible name of the button was provided by the <code>aria-label</code> attribute:</p>
<img src="https://sarasoueidan.com/assets/images/aria-label-button-name.png" alt="" />
<p>The devtools will also show when the inner text content of the button was overridden by another accessible name:</p>
<img src="https://sarasoueidan.com/assets/images/aria-label-button-name-overridden.png" alt="" />
<p>You can try it yourself by inspecting the following button:</p>
<button class="icon-button" aria-label="Menu">
<svg width="24px" height="24px" viewBox="0 0 24 24" focusable="false">
<path d="M21 11h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
<path d="M3 7h18c0.6 0 1-0.4 1-1s-0.4-1-1-1h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1z"></path>
<path d="M21 17h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
</svg>
<span style="color: inherit; margin-left: .5em; ">Bananas</span>
</button>
<p>It is important to note here that <strong>you do <em>not</em> want to have an accessible label in <code>aria-label</code> that is different from the visual text label.</strong> I’ve used different labels here for demonstration purposes only. Always make sure the visual text matches what screen reader users will hear; this is <a href="https://www.w3.org/WAI/WCAG21/quickref/#label-in-name">a WCAG 2.1 requirement</a>. Also worth noting here is that not all screen reader users are blind, so you’ll want to make sure that what they see matches what the screen reader is announcing to them.</p>
<h5 id="further-reading" tabindex="-1">Further Reading</h5>
<ul>
<li><a href="https://www.w3.org/TR/WCAG20-TECHS/ARIA14.html">ARIA14: Using aria-label to provide an invisible label where a visible label cannot be used</a></li>
</ul>
<h3 id="technique-%234%3A-aria-label-on-the-%3Csvg%3E-icon" tabindex="-1">Technique #4: <code>aria-label</code> on the <code><svg></code> Icon</h3>
<p>In all of the previous techniques, we avoided using the icon to provide an accessible name to the button. But we don’t have to do that. The icon itself can be used to create a label to the button, by making sure that the icon itself has an accessible label.</p>
<p>The quickest way to add a label to an <code><svg></code> icon is by, once again, using <code>aria-label</code> .</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-trigger<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 28<span class="token punctuation">"</span></span><br /> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Since the button doesn’t have any text content in it, nor does it have a label defined using any ARIA attributes, the icon itself will now be used as a name. Since the icon has a clear label, that label will be used as the accessible name for the button. This is why it’s important to keep in mind that the name you provide to the icon should describe what the button does, not what the icon is. For example, you wouldn’t want to label it ”hamburger icon”.</p>
<p>Note that since we are making use of the icon in this technique, we are no longer hiding it using <code>aria-hidden</code> , but we do want to prevent it from receiving unwanted focus with <code>focusable = "false"</code> .</p>
<p>VoiceOver will announce <q>Menu, Button</q>, and the devtools will now indicate that the accessible name of the button was provided by its content (the icon’s label):</p>
<img src="https://sarasoueidan.com/assets/images/icon-label-name.png" alt="" />
<p>You can try it yourself by inspecting the following button:</p>
<button class="icon-button">
<svg width="24px" height="24px" viewBox="0 0 24 24" focusable="false" aria-label="Menu">
<path d="M21 11h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
<path d="M3 7h18c0.6 0 1-0.4 1-1s-0.4-1-1-1h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1z"></path>
<path d="M21 17h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
</svg>
</button>
<p><em>Please note that I do not recommend using this technique as it fails in some browser / screen reader combinations. Consult the <a href="https://sarasoueidan.com/blog/accessible-icon-buttons/#there-is-no-one-way-to-rule-them-all">“There is No One Way to Rule Them All”</a> section below for details.</em></p>
<h3 id="technique-%235%3A-aria-labelledby-on-the-svg-icon" tabindex="-1">Technique #5: <code>aria-labelledby</code> on the <code>svg</code> Icon</h3>
<p>This technique is conceptually similar to Technique #4 above. But instead of using <code>aria-label</code> to provide a label to our svg icon, we will use <code>aria-labelledby</code> . It is worth noting that this is also is one of the best ways to make an inline svg image(s) accessible.</p>
<p>In this technique, <code>aria-labelledby</code> will reference the <code><title></code> of the SVG as the accessible label:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-trigger<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">focusable</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 28<span class="token punctuation">"</span></span><br /> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Menu <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>VoiceOver will announce <q>Menu, Button</q>, and the devtools will once again indicate that the accessible name of the button was provided by its content (the icon’s label inside the <code><title></code> element):</p>
<img src="https://sarasoueidan.com/assets/images/icon-labelledby-name.png" alt="" />
<p>You can try it yourself by inspecting the following button:</p>
<button class="icon-button">
<svg width="24px" height="24px" viewBox="0 0 24 24" focusable="false" role="img" aria-labelledby="svg-title">
<title id="svg-title"> Menu </title>
<path d="M21 11h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
<path d="M3 7h18c0.6 0 1-0.4 1-1s-0.4-1-1-1h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1z"></path>
<path d="M21 17h-18c-0.6 0-1 0.4-1 1s0.4 1 1 1h18c0.6 0 1-0.4 1-1s-0.4-1-1-1z"></path>
</svg>
</button>
<h5 id="further-reading%3A-3" tabindex="-1">Further Reading:</h5>
<ul>
<li><a href="http://www.sitepoint.com/tips-accessible-svg/">Tips for Creating Accessible SVG</a></li>
<li><a href="https://developer.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/">Using ARIA to enhance SVG accessibility</a></li>
</ul>
<p><em>Please note that I do not recommend using this technique as it fails in some browser / screen reader combinations. Consult the <a href="https://sarasoueidan.com/blog/accessible-icon-buttons/#there-is-no-one-way-to-rule-them-all">“There is No One Way to Rule Them All”</a> section below for details.</em></p>
<h3 id="use-svg" tabindex="-1">Use SVG</h3>
<p>You’ve already noticed that I used SVG to embed the icon in the buttons in this article. It is my personal belief that using SVG should be the first requirement to making icons accessible. There are there <a href="https://css-tricks.com/icon-fonts-vs-svg/">a myriad of reasons why SVG is better suited for icons than icon fonts</a>. If you’re using icon fonts and are not sure what it takes or how to make the switch, I have <a href="https://sarasoueidan.com/blog/icon-fonts-to-svg/">an article</a> that covers that, and that will help you make the switch in no time.</p>
<p>Making an element accessible does not only mean making it accessible to screen readers. It means making it <em>universally</em> accessible, regardless of what the user is using to interact with that element or what environment they’re in.</p>
<p>In addition to the many benefits you get from using SVG, doing so makes it easier to make your icons accessible in user-controlled environments, such as Windows High Contrast Mode. I’ll be sharing more about that in a separate article.</p>
<h3 id="there-is-no-one-way-to-rule-them-all" tabindex="-1">There is no One Way to Rule Them All</h3>
<p><a href="https://www.scottohara.me/">Scott O’Hara</a> and I synced together to publish two complementary articles today that hopefully provide you with everything you need to know about this topic.</p>
<p>In <a href="https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html">his article</a>, Scott dives into the technical intricacies of the markup patterns used to implement accessible images and svgs, and (generously) shares his extensive tests and findings on how those patterns work in different browser / screen reader pairings, or if they’re used within a button or a link. If you’ve made it this far, <a href="https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html">Scott’s article</a> is the next logical stop.</p>
<p>Scott’s test findings are extremely important as you will learn that treating the icon as decorative and providing an accessible name to the button using one of the first three techniques is often the better way to go. Either that or you will need to pick the svg pattern that has the test with the least amount of failures, and use that. In other words, <strong>both Technique #4 and Technique #5 come with their failures in some browser / screen reader combinations.</strong></p>
<p>Personally, I’d not use the SVG icon itself to provide a label for the button when I can provide one on the button itself directly. As Scott states, <q>there’s just no good way to use an SVG as the sole means to provide an accessible name to a link or button. Every test had failures.</q> Thankfully, Scott has saved us <em>hours</em> of testing, so if you <em>need</em> to provide a label to the button using the svg itsef, you can choose the pattern you need based on his findings.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>I hope this article has given you a clear overview of many possible ways to provide an accessible label to an icon button.</p>
<p>It is important to note, though, that we <em>only</em> covered providing an accessible <em>label</em> to the buttons. There are many more things to take into consideration when combining labels with additional descriptions (using <code>aria-describedby</code> , for example), so please beware of those, and make sure you properly test your buttons before pushing them to production.</p>
<p>If you’d like to learn more about creating accessible UI patterns, I’ve got a full-day workshop that you can sign up for. I’m running this workshop:</p>
<ul>
<li>at the Frozen Rockets academy in The Hague, The Netherlands, on June 10th, and</li>
<li>at SmashingConf in Freiburg, Germany, next September.</li>
</ul>
<p>You can <a href="https://academy.frozenrockets.nl/workshop/accessible-ui-patterns-10-june">register for the Frozen Rockets workshop</a> in The Hague today. Tickets are also on sale for <a href="https://smashingconf.com/freiburg-2019/workshops/sara-soueidan/">the SmashingConf workshop</a> in September.<strong>I would love for you to come join me!</strong></p>
<hr />
<p><em>Many thanks to my friend Scott O’Hara for proofreading this article, his feedback, and for following up with his own findings and test results.</em></p>
The SVG Filters Series
2019-01-15T00:00:00Z
https://sarasoueidan.com/blog/svg-filters-series/
<ul class="series" data-series="">
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/01/15/svg-filters-101/">SVG Filters 101</a></span>
<div class="series__item__description">
The first article in a series on SVG filters. This guide will help you understand what they are and show you how to use them to create your own visual effects.
</div>
</li>
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/01/22/svg-filter-effects-outline-text-with-femorphology/">SVG Filter Effects – Outline Text with <code><feMorphology></code></a></span>
<div class="series__item__description">
In this second part of the SVG Filter series you'll learn all about the <code>feMorphology</code> filter, how it works and how you can use it to create paint-like image effects and proper text outlines.
</div>
</li>
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/01/29/svg-filter-effects-poster-image-effect-with-fecomponenttransfer/">SVG Filter Effects – Poster Image Effect with <code><feComponentTransfer></code></a></span>
<div class="series__item__description">
In this third article in the series of articles about SVG Filters you'll learn all about the powerful <code>feComponentTransfer</code> operation and how it can be used to control the individual R/G/B/A components on a pixel level independently. As a practical example, we will be creating a poster image effect by using this primitive to limit the number of colors in an image.
</div>
</li>
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/02/05/svg-filter-effects-duotone-images-with-fecomponenttransfer/">SVG Filter Effects – Duotone Images with <code><feComponentTransfer></code></a></span>
<div class="series__item__description">
In this fourth article in the series of articles about SVG Filters you'll learn how to use <code>feComponentTransfer</code> to recreate Photoshop’s duotone image effect and control an image's luminance and color contrast.
</div>
</li>
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/02/12/svg-filter-effects-conforming-text-to-surface-texture-with-fedisplacementmap/">SVG Filter Effects – Conforming Text to Surface Texture with <code><feDisplacementMap></code></a></span>
<div class="series__item__description">
In this fifth article in the series of articles about SVG Filters you'll learn how to conform text to surface texture using an SVG displacement map similar to how you would do in graphics editors like Photoshop.
</div>
</li>
<li class="series__item">
<span class="series__item__title"><a href="https://tympanus.net/codrops/2019/02/26/svg-filter-effects-moving-forward/">SVG Filter Effects — Moving Forward</a></span>
<div class="series__item__description">
In this last article of the SVG Filter Effects series, I’ll share a list of useful resources and experiments to learn more about SVG Filters to start creating your own effects.
</div>
</li>
</ul>
The Refactoring UI Book
2019-01-02T07:36:45Z
https://sarasoueidan.com/blog/refactoring-ui/
<p class="deck">I’ve never taken or had any design classes in school or university. Most of my (humble) design knowledge I’ve picked up online from random articles here and there, and from breaking down and building designs that I liked. So, when I found <a href="https://twitter.com/steveschoger">Steve</a> on Twitter and saw how he was sharing design tips targeted at developers with no design background, I followed him in the blink of an eye. I'm not exaggerating when I say that he was one of my best follows in 2018.</p>
<p>I remember the day <a href="https://twitter.com/SaraSoueidan/status/981612024177287168">I asked him to make a book</a> out of all the tips he’d been sharing. I told him I’d give him my money in a heart beat. A book that contained all the useful tips he was sharing, <em>the way</em> he was sharing them, would have been a box full of gems.</p>
<p>Fast forward a few months, Steve and <a href="https://twitter.com/adamwathan">Adam</a> have published their <a href="https://refactoringui.com/book/">Refactoring UI book</a>, based on all the tips Steve was sharing, plus many more. And I’m not surprised to say that the book is just as good as I expected it would be.</p>
<img src="https://sarasoueidan.com/assets/images/refactoring-ui-book.png" alt="The Refactoring UI Book cover" />
<p>Just like his tweets, the book is <em>chock full</em> of design tips — brief, short tips written very well, straight to the point, and accompanied with visual respresentations of how to apply them and how <em>not</em> to. One tip is followed by another, without any extra talk or gibberish in between. <em>Just</em> my kind of content. <em>Just</em> my kind of book. As I was reading it, I couldn’t help but think that this is how I would write my own book, which is why I enjoyed reading it so much. And I read it <em>fast</em> because, again, there was no long or unnecessary talk — just the tips distilled down and lots of pretty visual eye candy.</p>
<p>I learned a <em>lot</em> reading the book. And my favorite part is that now I <em>understand</em> design decisions made when I see them. I look at Dribbble shots and I think “Ah yeah I see what the designer did there!” and why they did it.</p>
<p>If you’re a developer looking to improve your design skills to build more professionally-looking projects, or if you’re looking to understand more about design decisions made by designers on your team, then this book is a great piece to add to your library.</p>
<p>I got the full bundle (thanks, Steve 💜), which also comes with PDF files containing color swatches (I love these!), UI component design samples, icons, and font recommendations. There are also a couple of videos where Steve shows you how to design or refactor a UI and make it look better using the tips from the book. A choice of either the full package or the essentials (book + videos only) is available to purchase on <a href="https://refactoringui.com/book/">the site</a>.</p>
<p>So, this is what I think of the book. Would I recommend it? <em>Of course!</em> So if you do purchase it, I hope you find it just as useful as I did.</p>
<p>Thank you for reading.</p>
Nested Links Without Nesting Links
2018-09-07T09:51:51Z
https://sarasoueidan.com/blog/nested-links/
<p>Chris Coyier started <a href="https://spectrum.chat/thread/e45da96d-cd4d-4bd5-b785-44cee6b60e74">a thought exercise thread</a> last week asking the community how they would approach building nested links. I had the same requirement a couple of years ago when I was building the front-end foundation for <a href="https://smashingmagazine.com/">Smashing Magazine</a>. So I thought I’d write my response to Chris’s thread out in the form of a blog post.</p>
<h3 id="the-challenge" tabindex="-1">The Challenge</h3>
<p>The following video shows what an article on the home page’s list of Latest Articles behaves like. Take note of how the URL of the links change at the bottom left of the video recording as I hover over different areas of the clickable blog post. You’ll notice that each of the internal links in the post links to a different page than that of the post itself, while clicking anywhere else on the post item links to the full article.</p>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/smashingmag-nested-links.mp4" width="auto" style="max-width: 100%;">
Sorry, your browser doesn't support embedded videos.
</video></figure>
<p>Each article is clickable as a whole entity and would link to the article page. But we still wanted the links within the article item, such as the author name and any links inside the excerpt, to also work as regular links.</p>
<h3 id="native-markup-limitations" tabindex="-1">Native Markup Limitations</h3>
<p><a href="https://www.w3.org/TR/html5/text-level-semantics.html#the-a-element">According to the specification</a>, an <code><a></code> element’s content model specifically states that an <code><a></code> cannot contain any interactive descendants. An <code><a></code> element <em>is</em> interactive, and so therefore you cannot nest an <code><a></code> inside another <code><a></code>.</p>
<p>This means that this markup is invalid:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Link content that references</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> another link <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> within the main one.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>In fact, if you <em>do</em> use that markup, your main link is going to break into two separate links, with some of its text content being left out in the process.</p>
<p>The following image shows what the DOM looks like after the browser parses my nested links from the above example:</p>
<img src="https://sarasoueidan.com/assets/images/nested-links.png" alt="Screenshot of what the markup for our nested links looks like after being parsed by the browser." />
<p>The browser “breaks” when it finds an <code><a></code> before it finds the closing tag for the preceding <code><a></code>, to it takes it up on itself to close the previous <code><a></code></p>
<h3 id="the-object-hack" tabindex="-1">The <code>object</code> Hack</h3>
<p>Upon searching for ways to nest links — if any existed, I came across <a href="http://kizu.ru/en/fun/nested-links/">this article</a> by Roman Komarov, in which he explains his workaround for nesting links that he found when he was trying to nest links in one of his own projects.</p>
<p>Roman came up with a brilliant way to make nesting links possible by working around the browser’s restrictions, taking advantage of how it parses HTML, and fooling it into allowing the nested link inside the first one using the <code><object></code> element: the inner link is wrapped in an <code><object></code> and this <code>object</code> is given a random, invalid mime type.</p>
<p>Because of the way browsers handle <code>object</code> elements, the <code>object</code> element is going to be completely ignored, allowing the content inside of it to be displayed in its stead. In fact, the contents of the <code>object</code> are only there to be displayed as a fallback for when the <code>object</code> itself fails to display. So we <em>make</em> it fail, by providing an invalid type, and the browser then displays the content inside of it — our nested <code><a></code> — in its place, thus rendering the inner link inside the outer one. Note that this is only possible because:</p>
<blockquote>
<p>…after wrapping any HTML with such attributeless <code>object</code> we would get just a wrapper element for this content. But a wrapper with an unusual trait: any content inside of it would be treated by browser’s parser without looking at the object’s context. So, using this trait we can, finally, nest one link into another, separating them for a parser.</p>
</blockquote>
<p>Albeit being a <em>very</em> clever hack, this is still <strong>a hack</strong>. It comes with its own limitations, including browser support that you’d have to work around. (Check Roman’s article out for more details.)</p>
<p>This hack also wasn’t very practical for the Smashing Magazine use case. We shouldn’t have to set up a script to wrap every single inner link with an <code>object</code>. Aside from being a hassle, in my very lazy developer opinion, this — I assume — might also have performance implications. So I continued looking for other — hopefully less hacky — options.</p>
<h3 id="the-css-only-layered-links" tabindex="-1">The CSS-only Layered Links</h3>
<p>There’s a reason I say “layered” link, not “nested” link here. This technique fakes the behavior of nested links by overlaying one link on top of the whole item (article post in our case), and then elevating the remaining links on top of it.</p>
<p>This results in the whole item area linking to the URL of that link overlay. The remaining links are elevated so their pointer events are not blocked by the overlay, thus allowing them to be independently clickable.</p>
<p><a href="https://twitter.com/csswizardry">Harry Roberts</a> has <a href="http://jsfiddle.net/csswizardry/rxsna">a JSFiddle</a> demo of this technique.</p>
<p>For the following markup:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://www.google.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>feugiat vitae<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>, ultricies eget, tempor sit amet, ante. <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.apple.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Aenean fermentum<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Ut felis. Praesent dapibus, neque <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://www.facebook.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>id cursus faucibus<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://csswizardry.com/2013/02/introducing-csswizardry-grids/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-link__read-more<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more<span class="token entity named-entity" title="…">&hellip;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The styles (SCSS) look like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.post-link</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* Position any links in the post excerpt at the top of the `z-index` stack. */</span></span><br /><span class="highlight-line"> <span class="token selector">a</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /> <span class="token comment">/* Stretch the ‘read more’ link over the whole of the post.<br /> * Hide the ‘read more’ link’s text. */</span><br /><span class="highlight-line"> <span class="token selector">.post-link__read-more</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">text-indent</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /> <span class="token comment">/* Needs a heightened specificity to trump `.post-link a`.<br /> * Stack it under all other links in the post text. */</span><br /><span class="highlight-line"> <span class="token selector">a&</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>The following image shows a very rough visual representation of this technique:</p>
<img src="https://sarasoueidan.com/assets/images/layered-links.png" alt="A rough illustration of the layered links" />
<p>Albeit brilliant, this technique had another issue that I wasn’t okay with: the keyboard tabbing order was, well, <em>out</em> of order. Here is a video showing what tabbing through the links Harry’s demo looks like:</p>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/bad-layered-links.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video></figure>
<p>Because we have a Read More link that overlays the whole post, the tabbing order goes like this:</p>
<ul>
<li>Tab to child link</li>
<li>Tab to child link</li>
<li>Tab to child link</li>
<li>Tab (whole) post</li>
<li>[tab to move to next post:]</li>
<li>Tab next post’s child link</li>
<li>Tab next post’s links</li>
<li>Tab next post’s link</li>
<li>Tab (whole) next post</li>
</ul>
<p>This tabbing order felt very unnatural to me, so I knew I would have to adjust this technique to fix that.</p>
<h3 id="my-implementation" tabindex="-1">My Implementation</h3>
<p>Harry’s technique provides everything I need for the Smashing use case, except for my expected tab order, especially given the markup on the magazine differed slightly from the markup Harry was working with:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>7 days ago<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- our first link 👇🏻 --></span>></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/2018/08/desktop-wallpaper-calendars-september-2018/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>This is our post title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>by <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/author/cosima-mielke<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Author Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> This is the post excerpt which very well contains <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>links<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> to places.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read More<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>The first link in the post markup is the title of the post which, naturally, links to the post itself.</p>
<p>So I thought: what if, instead of overlaying the last link in the post — the Read More link — over the post, I would overlay the first link instead?</p>
<p>Getting <em>the title of the post</em> to expand and cover the entire post area would mean that that area would also link to the post, which is exactly what we want to do in the first place!</p>
<p>And since we’re not overlaying the last link on top of the links prior to it, this fixes the tab order and we end up with tabbing that works like this:</p>
<figure class="video wide"><video controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/assets/videos/good-layered-links.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video></figure>
<p>The user tabs through the posts by going through the title, its following “child” links, then the Read More link, then moves on to the title of the following post, its child links, its Read More link, and so on. We’ve surpassed the backwards jump where the entire post was re-focused after focusing its contents. (Usually, tabbing would happen the other way around: focus the container, then tab through its content.)</p>
<p>Technically, the post title will be expanded to cover the post by expanding a <code>::before</code> pseudo-element inside of, which practically expand the entire title’s box with it:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.the-post</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* elevate the links up */</span></span><br /><span class="highlight-line"> <span class="token selector">a</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.the-post-title</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">a</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> static<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* expand the pseudo-element to cover the post area */</span></span><br /><span class="highlight-line"> <span class="token selector">&::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> ...<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="ux-%26-usability-considerations" tabindex="-1">UX & Usability Considerations</h3>
<p>You may have already noticed the major limitation to this technique: the post excerpt’s text is covered by the pseudo-element, which makes that text unselectable. If you try to select the text to copy it, you won’t be able to.</p>
<p>This was a compromise the Smashing team were okay with after testing the design with their users. The inability to select the text wasn’t an issue, as not many users attempted to make that selection.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>Calling these links “layered links” is a lot more appropriate and descriptive than “nested links”, given how they are implemented/styled.</p>
<p>I would consider this technique “hacky” for sure, but if it works, and <strong>as long as it does not compromise the overall accessibility of the design</strong>, then I see no reason not to use it.</p>
<p>The most important takeaway from this is to remember to always test the usability and accessibility of your design, especially if you use unconventional CSS tricks to create it.</p>
<p>If you’re interested in some of the other tricks I used during the Smashing magazine development, <a href="https://vimeo.com/223433196">check out the video of my talk</a>, or <a href="https://sarasoueidan.com/case-studies/smashing-magazine">read the case study</a> which includes a link to the talk slides.</p>
<p>I hope you found this article useful.</p>
<p>Cheers,<br />
Sara</p>
How do you mark up an accordion?
2018-09-03T10:54:24Z
https://sarasoueidan.com/blog/accordion-markup/
<p class="deck">
I made <a href="https://twitter.com/SaraSoueidan/status/1035476406334054400">a poll on Twitter</a> the other day asking the #lazyweb how they would mark up an FAQ section — or a list of questions and their corresponding answers. I specifically asked for <em>markup</em> suggestions. Turns out, people mark questions and answers up differently. I got some interesting insight from the responses I got that partly changed the way I would approach building an FAQ section, and some validation for the way I always have built them. The discussion was too interesting to not summarize in an article. The different possible markup approaches as well as useful resources are discussed below.
</p>
<h3 id="no-javascript" tabindex="-1">No JavaScript</h3>
<p>The requirement for answering this question was to think of the answer in a no-JavaScript case.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Hey <a href="https://twitter.com/hashtag/lazyweb?src=hash&ref_src=twsrc%5Etfw">#lazyweb</a>, I got a small Friday poll for you:<br /><br />How do you mark up a list of questions and answers? (Think: FAQ)<br /><br />I’m talking about semantic markup, so forget about accordions for a second and assume there is no JS for that.<br /><br />Would you use:</p>— Sara Soueidan (@SaraSoueidan) <a href="https://twitter.com/SaraSoueidan/status/1035476406334054400?ref_src=twsrc%5Etfw">August 31, 2018</a></blockquote>
<p>Why?</p>
<p>Usually, FAQs are eventually turned into an accordion. Accordions require JavaScript to work (with the exception of the native <code><details></code> element which I’ll talk about shortly), and that JavaScript, if designed well, can be applied to almost any kind of proper markup — regardless of what elements are used to mark the questions and answers up. That’s why I specifically asked people to think about HTML semantics, not interactive behavior; otherwise, the one answer I would have probably gotten would have been “accordion”.</p>
<p>I believe that it is imperative to think about how a component would render if no JavaScript behavior was available if you want that component to be truly accessible. Semantic HTML is the foundation of truly inclusive content. Start with the bare bones — the markup, and think about how it would render if no CSS or JS were enabled. <em>Then</em>, enhance the component by adding interactive behavior to it using JavaScript, making sure that you don’t sacrifice the accessibility of it in the process. This usually means applying and using proper ARIA roles and attributes, <a href="https://developer.paciellogroup.com/blog/2018/06/short-note-on-progressive-aria/">progressively applied</a>, via Javascript.</p>
<p>An FAQ can be thought of as a <em>list</em> of questions with answers, or a <em>series</em> of questions and answers.</p>
<h3 id="a-series-of-questions-and-answers%3A-hx-%2B-div" tabindex="-1">A series of questions and answers: hx + div</h3>
<p>A series of questions and answers can be marked up by using a series of headings for the questions, and paragraphs (probably wrapped in a <code><div></code>) for each answer. This is one of the most commonly used code patterns.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>How would you mark up a series of questions and answers?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I would probably use headings, each followed by an answers most likely wrapped in a semantic-less wrapper.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>How would you mark up a series of questions and answers?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I would probably use headings, each followed by an answers most likely wrapped in a semantic-less wrapper.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>How would you mark up a series of questions and answers?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I would probably use headings, each followed by an answers most likely wrapped in a semantic-less wrapper.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The whole series of questions and answers would then also be wrapped in a container, or each pair of question and answer can be wrapped in an <code><article></code>, and they can easily be scripted to add accordion behavior by potentially replacing the heading text content with a toggle <code><button></code> which controls the expanded/collapsed answer “panel”. ARIA attributes would be used to establish the relationship between the question’s toggle button and the associated answer, and icons can also be added to the button inline using JavaScript as well.</p>
<p class="note">
The enhancement of the markup to an interactive accordion is outside the scope of this article. Refer to the Further Resources section below for links to learn more about adding accordion interactivity to basic markup.
</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myID-1<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>How would you mark up a series of questions and answers?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- svg icon here, styled and positioned using CSS --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myID-1<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I would probably use headings, each followed by an answers most likely wrapped in a semantic-less wrapper.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> </span></code></pre>
<p>The way you might approach turning it into an accordion might be slightly different, but the requirements for making an accordion accessible should still be applied, so, in essence, the final result wouldn’t be too different.</p>
<p><strong>Advantages of this approach:</strong></p>
<ul>
<li>The markup is simple and can be easily converted into an interactive accordion. ✔</li>
<li>Assistive technology users are able to navigate the FAQ using the headings. ✔</li>
</ul>
<h3 id="a-list-of-questions-with-answers%3A-ul-or-dl" tabindex="-1">A list of questions with answers: ul or dl</h3>
<p>If you think about FAQs as <em>a list of questions with answers</em>, you’ll probably want to use HTML lists to mark them up.</p>
<p>I’d probably not use an ordered list (<code><ol></code>) because the order of the questions is usually irrelevant. That leaves us with unordered lists (<code><ul></code>) and definition lists (<code><dl></code>).</p>
<p>What both lists have in common is the semantics of a list — meaning that the questions and answers would be conveyed to assistive technologies as a list of items.</p>
<h4 id="an-unordered-list-of-questions-with-answers%3A-ul-li-%2B-hx-div" tabindex="-1">An unordered list of questions with answers: ul li + hx div</h4>
<p>If you use an unordered list, each list item would contain both the question and the question’s corresponding answer.</p>
<p>The semantics of an unordered list item <code><li></code> would not be enough to represent and distinguish the question and/from the answer. So I’d consider wrapping the question in a heading. This does feel odd, as I’ve never thought that an <code><li></code> as a container to a heading, but this code has some important benefits.</p>
<p>I’d next wrap the content of the answer in a <code><div></code> so that I can easily toggle it when I want to add accordion functionality to the FAQ.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>What is a bird’s favorite sleeping spot?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Their owner’s shoulders and foreheads. They like to get warmth from a hooman’s neck and face.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> They also like always reminding you that they’re the boss.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p><strong>Advantages of this approach:</strong></p>
<ul>
<li>Assistive technologies (ATs) will announce the number of items, so their users will get an overview of how many questions there are. ✔</li>
<li>AT users will be able to navigate the FAQ list using the headings. ✔</li>
<li>It is also fairly easy to turn into an accordion using JavaScript and ARIA, similar to what we saw in the previous section. ✔</li>
</ul>
<h4 id="a-definition-list" tabindex="-1">A definition list</h4>
<p>A definition list is similar to ordered and unordered lists. What distinguishes it from other lists is that it is made up of key/value pairs.</p>
<blockquote>
<p>The HTML <code><dl></code> element represents a description list. The element encloses a list of groups of terms (specified using the <code><dt></code> element) and descriptions (provided by <code><dd></code> elements). Common uses for this element are to implement a glossary or to display metadata (a list of key-value pairs).</p>
<p>—MDN</p>
</blockquote>
<p>I have always used definition lists (<code><dl></code>) to mark up FAQs. I like the fact that a <code><dl></code> has a series of terms (<code><dt></code>) and descriptions (<code><dd></code>). A question would be a term. The answer would be its description.</p>
<p>This approach got the most votes in my poll. This may partially be due to the fact that <a href="http://w3c.github.io/html/grouping-content.html#the-dl-element">the specification mentions</a> questions and answers as an example usage for definition lists:</p>
<blockquote>
<p>Term-description groups may be names and definitions, <strong>questions and answers</strong>, categories and topics, or any other groups of term-description pairs.</p>
</blockquote>
<p><em>(Emphasis mine)</em>:</p>
<p>Markup for a <code><dl></code>-based FAQ would look like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Your question here?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>You answer here. It can be anything and as long as you want, and contain any type of content.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Your question here?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>You answer here. It can be anything and as long as you want, and contain any type of content.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<p>Converted to an interactive accordion, and similar to what we’ve seen before, it would look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myID-1<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your question here?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myId-1<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>You answer here. It can be anything and as long as you want, and contain any type of content.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myID-2<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your question here?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myId-2<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>You answer here. It can be anything and as long as you want, and contain any type of content.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<p>I’ve never hit any road blocks styling definition lists, but that’s probably because none of my projects required any styles that were not possible using the default markup. But many developers have, as the Twitter thread shows.</p>
<p>Most styling limitations were due to the fact that it was invalid HTML to have a <code><div></code> as a child of the <code><dl></code>. This, in turn, meant that it would be difficult to group and style pairs of terms and descriptions.</p>
<p>Today, it is perfectly <a href="http://w3c.github.io/html/grouping-content.html#the-dl-element">valid</a> to add a <code><div></code> as a child to a <code><dl></code>. That said, <a href="https://twitter.com/stevefaulkner">Steve Faulkner</a> points out that “this can cause issues for screen readers as the pattern changes the way the semantics are represented in some browsers.” Check out <a href="https://s.codepen.io/stevef/debug/GxwaoP">his test cases and results</a> for more context and details.</p>
<p><strong>Notes about this approach:</strong></p>
<ul>
<li><em>Some</em> assistive technologies (ATs) will announce the number of items, so users of those technologies get an overview of how many questions there are. This, however, is unfortunately <a href="https://codepen.io/aardrian/full/NzGaKP/">not consistent across all tech</a>.</li>
<li>AT users will <em>not</em> be able to navigate the questions like they could with headings.</li>
<li>It is fairly easy to turn into an accordion using JavaScript and ARIA. ✔</li>
</ul>
<p>Even though I will not be using them for marking up FAQs from now on after the discussions I had as a result of this poll, I would still use definition lists for other, more proper use cases. For example, I’ve used them before within articles where I list a set of available attributes or properties and then elaborate on each one and describe what it does.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/dl-example.png" alt="Screenshot of an example usage of a definition list from one of my articles." />
<figcaption>
My <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">SVG Coordinate Systems guide</a> contains a perfect use case for definition lists.
</figcaption>
</figure>
<h3 id="the-native-html-accordion%3A-details-%2B-summary" tabindex="-1">The native HTML accordion: details + summary</h3>
<blockquote>
<p>The <code><details></code> element represents a disclosure widget from which the user can obtain additional information or controls.</p><p></p>
<p>– <a href="https://www.w3.org/TR/html51/interactive-elements.html#the-details-element">W3C HTML specification</a></p>
</blockquote>
<p>Inside <code><details></code>, we can put any sort of content we want.</p>
<p><code><details></code> has a friend called <code><summary></code>. When present inside <code><details></code>, the first <code><summary></code> element child represents the summary or legend of the <code><details></code>.</p>
<p>Essentially, we can use <code><details></code> to create an accordion-like widget that the user can toggle open and closed. The <code><summary></code> element, when present, acts as the content of the toggle for the accordion.</p>
<p>Before doing the Twitter poll, I’d never considered using <code><details></code> and <code><summary></code> to mark up FAQs. I’ve always thought of the semantics of these elements too literally: a summary is, well, a summary of the content of the details. Since the summary of an answer is derived from that answer itself then it is also an answer; and then a question could not be a <summary> because the question is not an answer. Confused yet?</summary></p>
<p>I had a discussion about this with my friend <a href="https://twitter.com/scottohara">Scott O’Hara</a> after tweeting the poll. We both made valid points, but the main reason we were confused and were disagreeing is that I was talking about <code><summary></code> as a “summary” whereas Scott was referring to the <code><summary></code> element as, well, a generic “toggle” element.</p>
<p>Turns out, and I‘ve finally made my peace with the fact, that <code><details</code>> and <code><summary></code> don’t literally represent content and summary of that content. The spec editors settled for these names out of a bunch of other options they had. Because naming things is hard.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Because expressing perfect meaning in a single small, easy-to-spell word is hard, so we approximate.<br /><br />I remember there being a lot of possibilities when naming that - <spoiler> was my favorite. 😀</spoiler></p>— 🌺Taudry Hepburn🌺 (@tabatkins) <a href="https://twitter.com/tabatkins/status/1035560054768857089?ref_src=twsrc%5Etfw">August 31, 2018</a></blockquote>
<p>I would have preferred <code><toggle></code> and <code><panel></code> as truly generic names. But if <code><details></code> and <code><summary></code> are just meant to be generic names, then using them to mark up FAQs starts to make a tiny bit more sense.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I think of it as: the "summary" is a short description of what you will see if you expand the details. A question in a FAQ is a summary of what the answer will cover.<br /><br />But yeah, I like toggle/panel more. Someone should have got you on the committee. But it's too late now.</p>— Amelia Bellamy-Royds (@AmeliasBrain) <a href="https://twitter.com/AmeliasBrain/status/1035584167327621120?ref_src=twsrc%5Etfw">August 31, 2018</a></blockquote>
<p>So, marking an FAQ up using <code><details></code> and <code><summary></code> is pretty simple:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Why is naming things so hard?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Because it just is.</></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Why is naming things so hard?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Because it just is.</></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>… and it comes with <strong>some nice advantages, and some limitations:</strong></p>
<ul>
<li>You get the accordion behavior (collapsible panels) baked in by default. ✔</li>
<li>The accordion is mostly fully accessible (ATs and keyboard) by default. Scott O’Hara wrote and shared <a href="https://www.scottohara.me/blog/2018/09/03/details-and-summary.html">the results of his tests</a> with popular screen readers. Make sure to read his post before deciding to use <code><details></code> as an accordion, as there are some things you’ll need to be aware of:</li>
</ul>
<blockquote>
<p>For general use within the context of a larger section of content, details and summary are well supported. However, if you want to treat them as a more complex accordion component, or need to support Internet Explorer and Edge, you’re going to need some JavaScript (and ARIA attributes for IE/Edge).</p>
</blockquote>
<ul>
<li>The <code><details></code> element and its <code><summary></code> are fairly easily styled. (Some specific browser handling is required but nothing too complex.) ✔</li>
<li>You <em>can’t</em> currently animate the opening and closing of the details panel. But you can <a href="https://codepen.io/brianhaferkamp/pen/jywbVZ">work around</a> it by animating the content inside the <code>details</code>.</li>
<li>Opera Mini, IE and Edge currently don’t <a href="https://caniuse.com/#feat=details">support</a> <code><details></code> and <code><summary></code>. But,</li>
<li><code><details></code> and <code><summary></code> degrade gracefully to show their content by default, without the interactive behavior, which, in my opinion, is a perfectly acceptable fallback experience.</li>
</ul>
<h3 id="so%2C-why-does-this-all-even-matter%3F" tabindex="-1">So, why does this all even matter?</h3>
<p>Because <a href="https://twitter.com/Mandy_Kerr/status/1027561153138896897">semantic HTML matters</a> and <a href="https://medium.com/@mandy.michael/building-websites-for-safari-reader-mode-and-other-reading-apps-1562913c86c9">is essential for creating truly inclusive and functional Web sites</a> that work across the widest range of apps possible.</p>
<p>Sure, there are different ways for doing one thing, but as long as all these different ways offer true accessibility, then we have the freedom to choose whichever technique we like.</p>
<p>It’s always necessary, in my opinion, to consider what content would render and look like in foreign environments, or in environments that are not controlled by our own styles and scripts. Writing semantic HTML is the first step in achieving truly resilient Web sites and applications.</p>
<h3 id="further-resources" tabindex="-1">Further Resources</h3>
<p>If you need to learn more about creating accordions that are accessible, I recommend checking these resources out:</p>
<ul>
<li><a href="https://www.scottohara.me/blog/2017/10/25/accordion-release.html">Accessible ARIA Accordions</a> by Scott O’Hara.</li>
<li><a href="https://inclusive-components.design/tabbed-interfaces/">Collapsible Sections</a> by Heydon Pickering.</li>
<li><a href="https://davatron5000.github.io/a11y-nutrition-cards/components/accordion">Accordion</a> accessibility expectations by Dave RUpert.</li>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#accordion">Accordion WAI-ARIA Authoring Practices</a></li>
</ul>
<p>Thank you for reading.</p>
<p>—Sara</p>
<p class="note">
Huge thanks to everyone who contributed to the Twitter thread with insightful content, as well as to my friend Scott O’Hara, who has taught me, and still teaches me, so much about accessibility.
</p>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
On Switching from HEX & RGB to HSL
2018-08-12T11:31:26Z
https://sarasoueidan.com/blog/hex-rgb-to-hsl/
<p class="deck">A couple of weeks ago I tweeted about a feature that I didn’t know existed in VS Code: the visual color editor that pops up when you hover over color values in a style sheet.</p>
<figure>
<video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/videos/hsl-editor.mp4" width="100%">
Sorry, your browser doesn't support embedded videos.
</video>
<figcaption>The VS Code color editor that pops up when you hover over a color value. The editor allows you to visually choose and edit colors, and to switch from one color format to another.</figcaption>
</figure>
<p>In the context of the tweet I mentioned that I had converted all the colors on my site from the Hexadecimal and RGB(A) formats to the HSL format. A bunch of folks asked me why I made this switch. This article is my response.</p>
<p>I’ve been using the Hexadecimal and RGB(A) color formats in my CSS for as long as I can remember. But I’ve never found these formats intuitive or easy to read, let alone tweak. I’ve always felt crippled whenever I needed to tweak the colors for contrast, or create colors that work well with other colors. I always resorted to GUIs, which made my workflow less than optimal. Until a while back when I came across <a href="https://medium.com/@mwichary/dark-theme-in-a-day-3518dde2955a">a great article</a> in which Marcin Wichary shows how he took advantage of the <code>hsl()</code> function in CSS to create different shades of colors for a dark theme for an app he was working on. That’s when I had an epic facepalm moment: how did I forget about HSL?!</p>
<p>I knew the <code>hsl()</code> function existed in CSS — heck, I even wrote <a href="https://codrops.com/">an entry about it in the Codrops CSS Reference</a>, but I don’t know why I never took advantage of it before now.</p>
<p>When I think about it, I guess it all goes back to my <em>“Use it only when you need it”</em> principle. <strong>I only introduce tools into my workflow when they offer great value for my projects.</strong> I’ve never given too much thought for the color scheme on my site — especially since I love black and white and have been sticking to them since forever, with one or two highlight colors for links and buttons here and there. So I never really needed a more powerful color system, despite the frustration I had whenever I tweaked the few colors I use on the site.</p>
<p>But with more sections added to the site during the last couple of years, and a few more sections I’m going to add later, and that will have their own set of colors, it became clear to me that color management in the CSS was going to get out of hand, and that I needed a more intuitive and sensible way to choose and tweak colors, this was particularly true because I also want to offer a couple of themes to make my site’s UI friendlier. Enter HSL.</p>
<h3 id="hsl-%3D%3D-hue%2C-saturation%2C-lightness" tabindex="-1">HSL == Hue, Saturation, Lightness</h3>
<p>In CSS, an HSL color can be expressed using the <code>hsl()</code> function:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>33<span class="token punctuation">,</span> 80%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>The first parameter is the hue value, the second parameter is the saturation value, and the third is the lightness value. The function also has another variation: <code>hsla()</code>, which takes the same parameters, with an addition fourth parameter that controls the opacity of the color.</p>
<p>The best way to understand HSL if you’re not familiar with it is to consult a color wheel.</p>
<img src="https://sarasoueidan.com/assets/images/color-wheel.png" alt="Hue angles on a color wheel" style="display: block; max-width: 400px; margin: 0 auto;" />
<br />
<h5 id="hue" tabindex="-1">Hue</h5>
<p>The Hue determines what color of the rainbow something is. <strong>The value of the hue is the angle of that color on the color wheel.</strong> Red starts at 0 degrees, and the rest of the colors follow along the 360 degrees. (You don’t need to specify the unit of the angle in the <code>hsl()</code> function because it is the default.)</p>
<p><video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/videos/hue.mp4" width="100%" style="max-width: 300px; margin: 1em auto 2em;">
Sorry, your browser doesn’t support embedded videos.
</video></p>
<h5 id="saturation" tabindex="-1">Saturation</h5>
<p>Colors can be vivid (rich) or dull. The less of the color there is, the more is turns into a shade of grey, depending on the hue and lightness you start with. Somewhere along the line — between the 100% pure hue and the shade of grey, you can see tinted grey, or, if you look at it the other way around, you see a dull hue. <strong>Saturation controls how vivid or dull a color is.</strong></p>
<p><video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/videos/saturation.mp4" width="100%" style="max-width: 300px; margin: 1em auto 2em;">
Sorry, your browser doesn’t support embedded videos.
</video></p>
<h5 id="lightness" tabindex="-1">Lightness</h5>
<p>HSL works by tinting colors with white, which is also similar to how I used to deal with colors back in my days of watercolor painting. If I wanted to make a color lighter, I’d add white. If I wanted to make it darker, I’d add black. This is how the Lightness parameter works, where going below 50% means you’re starting to add black to the hue and creating a new <em>shade</em> of the color, and going above 50% means you’re adding white, creating a <em>tint</em>.</p>
<p><video class="video-gif" controls="" autoplay="" loop="" muted="" playsinline="" src="https://sarasoueidan.com/videos/lightness.mp4" width="100%" style="max-width: 300px; margin: 1em auto 2em;">
Sorry, your browser doesn’t support embedded videos.
</video></p>
<blockquote>
<p>
In color theory, a tint is the mixture of a color with white, which increases lightness, while a shade with black, which reduces lightness. A tone is produced either by the mixture of a color with grey, or by both tinting and shading.[1] Mixing a color with any neutral color (including black, gray and white) reduces the chroma, or colorfulness, while the hue remains unchanged.
</p>
<cite>— <a href="https://en.wikipedia.org/wiki/Tints_and_shades">Wikipedia</a></cite>
</blockquote>
<p>When you define a color in the HSL format, you pick a color in the form of an angle between 0 and 360. Setting the saturation to 100% and the lightness to 50% you get the purest form of that color. Tweaking the color from there on becomes very intuitive.</p>
<h3 id="hsl-and-color-harmonies" tabindex="-1">HSL and Color Harmonies</h3>
<p>Using the color wheel to pick colors has many benefits. One of the main advantages of HSL is that creating color harmonies becomes a piece of cake.</p>
<p>Complementary colors are located across from one another on the wheel. So if you start with a color and you want to get its complimentary one, all you need to do in CSS is to add 180° to the value of the Hue:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token property">--primary-color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>257<span class="token punctuation">,</span> 26%<span class="token punctuation">,</span> 42%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token property">--complementary-color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>437<span class="token punctuation">,</span> 26%<span class="token punctuation">,</span> 42%<span class="token punctuation">)</span><span class="token punctuation">;</span> // 257 + 180 === 257 - 180</span></code></pre>
<p>The first thing you’ll probably notice in this example is that my complementary hue angle is greater than 360. Guess what? That’s okay, because HSL is smart enough to loop around the color wheel again. You could also have deducted 180 degrees from the primary hue if you didn’t want to add them.</p>
<!-- <figure> -->
<img src="https://sarasoueidan.com/assets/images/color-harmonies-2.png" alt="Color Harmonies" />
<!-- <figcaption style="text-align: center; margin-top: 1em">Color Harmonies.</figcaption> -->
<!-- </figure> -->
<p>There are many color harmonies (or <em>schemes</em>) as the image above shows. Similar to what we did above with the complimentary scheme, triadic color schemes can be created by adding (or subtracting) 120°. You can also create analogous color combinations with 30° separating the hues. You can also create monochromatic harmonies easily, with one main hue and then tweaking the lightness to get different tints and shades of that hue. The sky is the limit.</p>
<h3 id="the-switch" tabindex="-1">The Switch</h3>
<p>The switch itself was super quick. I installed a Sublime Text plugin that converts all of my colors to HSL format in a fraction of a second. That’s all it took. Yay 🙌🏻 to useful tools (and to the brilliant people who make them).</p>
<h3 id="hsl-%2B-css-custom-properties-%3D-%F0%9F%92%9C" tabindex="-1">HSL + CSS Custom Properties = 💜</h3>
<p>CSS Custom Properties (a.k.a CSS Variables) are the <em>best</em> when it comes to creating multiple themes that can be applied on the fly. They:</p>
<ul>
<li>are live variables, available at run time. This makes it possible to change and update the values of these variables on the fly, and the changes will be reflected on the page without refreshing it.</li>
<li>can be inlined in a style attribute, in a style tag, or updated within a style sheet — all live, just like any other CSS property. This means that you don’t need to request a separate style sheet for the different themes.</li>
</ul>
<p>You can define a set of colors for each theme and then “activate” the theme by updating the value of a data attribute on the root element. Or you could use class names, if you prefer. The value of the theme data attribute is updated upon user’s interaction with a set of inputs specifically created for changing the theme.</p>
<p>You can use a CSS Variables as a value inside another variable. And you can use CSS Variables in combination with the calc() function. This, combined with hsl(), wields a lot of power when it comes to creating, maintaining and tweaking site-wide color themes.</p>
<p>Marcin already showed a great example in his article that I recommend checking out if you haven’t already. I imagine creating different themes leveraging CSS Variables and HSL <em>could</em> look something like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">html</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* ...some styles... */</span></span><br /><span class="highlight-line"> <span class="token property">--hue</span><span class="token punctuation">:</span> 257<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">--complimentary-hue</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span> <span class="token function">var</span><span class="token punctuation">(</span>--hue<span class="token punctuation">)</span> - 180<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* The styles here are mostly random. Don’t judge. */</span></span><br /><span class="highlight-line"> <span class="token property">--background</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--hue<span class="token punctuation">)</span><span class="token punctuation">,</span> 26%<span class="token punctuation">,</span> 42%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--background-dark</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--hue<span class="token punctuation">)</span><span class="token punctuation">,</span> 26%<span class="token punctuation">,</span> 28%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">--button-background</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--complimentary-hue<span class="token punctuation">)</span><span class="token punctuation">,</span> 26%<span class="token punctuation">,</span> 55%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* etc. */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">html[data-theme='green']</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">--hue</span><span class="token punctuation">:</span> 128<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">html[data-theme='pink']</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">--hue</span><span class="token punctuation">:</span> 313<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can organize the code in <em>any</em> and <em>many</em> different ways, all depending on your own project and needs. This is just a simple example showing how HSL can be used to create more maintainable and readable color swatches and relationships, especially when combined with CSS Variables. I’ll come back with another article and a real life example another time. 😌</p>
<h3 id="useful-resources" tabindex="-1">Useful Resources</h3>
<ul>
<li>
<p><a href="https://tympanus.net/codrops/css_reference/hsl/">hsl() Codrops CSS Entry</a></p>
</li>
<li>
<p><a href="https://chromatichq.com/blog/understanding-and-using-hsl-your-css">Understanding and Using HSL in your CSS</a></p>
</li>
<li>
<p>Marcin Wichary’s <a href="https://medium.com/@mwichary/dark-theme-in-a-day-3518dde2955a">Dark Theme in a Day</a></p>
</li>
</ul>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>I’ve been meaning to create a living style guide for my site for over a year now. But most of us know that our own Web sites usually sit on the shelf with a lower priority compared to client projects, and this is particularly true for freelancers. We’d rather be spending the time working on client projects and making a living. But what we tend to neglect is the fact that our sites are our online home, the place clients visit to enquire about work, and it’s important that they reflect the kind of quality and organization that we promise to offer our clients.</p>
<p>I’m currently working on cleaning up the code and styles of my site, organizing the CSS files by creating componentized styles, and, eventually, working my way into creating a living style guide.</p>
<p>With HSL, I now feel like I have real control over color management — every aspect of it, from choosing colors to controlling how they relate to and work with each other. Creating color relationships and swatches for theming has never been more intuitive or as easy for me.</p>
<p>I’ve recently <a href="https://www.youtube.com/watch?v=VYWPH8QGuGE&list=PL4CMETp4gcvMWLqdQlz5gl2GerF2WH7RU&index=2&t=0s">learned that Github also uses HSL</a> as their color format. I really like the tip of how they moved from a dull, grey-grey into a more blue-tinted grey, which is made possible by choosing the blue hue you want and then desaturating it enough to get to the level of grey you want out of it, and decreasing its lightness to darken it as needed.</p>
<p>HSL is a very powerful color format, especially when combined with other CSS features like CSS Variables. And I intend to take full advantage of it from now on. I’ll share any special tips or tricks that I learn or come up with in my process as I go.</p>
<p>Cheers, <br />
Sara</p>
SVG Filters: The Crash Course
2018-08-11T00:47:34Z
https://sarasoueidan.com/blog/svg-filters/
<p class="deck">I always try to customize in-house client <a href="https://sarasoueidan.com/workshops/">workshops</a> to my client’s needs. That sometimes also means that if my client’s design and dev team is interested in learning something that is not covered in my workshop, I will tweak the content of the workshop to make sure they learn what they need to make their work better.</p>
<p>One such example is when <a href="http://localhost:1313/blog/netflix-workshop/">I ran my SVG Workshop for the design and engineering team at Netflix</a> last year. <a href="https://twitter.com/Elijah_Meeks">Elijah</a> told me he’d be interested in learning more about SVG Filters. I hadn’t dug into SVG filters before that, so I took his request as an opportunity to finally do it, so that I could customize the workshop for them.</p>
<p>I learned all the basics of SVG filters in time for the workshop, and then spent the following two months learning more. The material I gathered, learned and created turned out to be useful enough to share with others, so I thought I’d write a series of articles on the topic, maybe even give them their own URL somewhere. I also thought they’d be good material for an interesting talk, so I created a talk and gave that talk twice this year: the debut was at <a href="https://beyondtellerrand.com/">Beyond Tellerrand</a> Munich in January, and the second one was at <a href="https://cssday.nl/">CSS Day</a> in Amsterdam last month.</p>
<p>If you know me, you know that my talks tend to be fast, packed with practical and technical goodies and tips. This talk was no exception. And the feedback to both talks was mind-blowing.</p>
<p>I had not shared the slides for the talk before today. The main reason was that I wanted to create that series of articles that I’d been meaning to write on the topic. But that plan never saw the day of light, and, to be quite frank, I don’t think that it will any time soon because I don’t feel as excited to write long, deep dives about SVG as I used to. My interest and excitement is shifting to other aspects of the front-end. I’m still very much into SVG but only as one of the many tools in our front-end arsenal, and my future articles are going to reflect that.</p>
<p>So I decided to write this article to share the video(s) of the talk(s), the slides, and some links for if you’re interested in learning more about SVG Filters from further resources.</p>
<h3 id="the-video-recording" tabindex="-1">The Video Recording</h3>
<p>The following is the video recording of my talk at btconf:</p>
<div class="l-aspect-ratio wide">
<iframe src="https://player.vimeo.com/video/251312501" width="640" height="360" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
</div>
<p><a href="https://vimeo.com/251312501">SVG Filters: The Crash Course - Sara Soueidan - btconfMUC2018</a> from <a href="https://vimeo.com/beyondtellerrand">beyond tellerrand</a> on <a href="https://vimeo.com/">Vimeo</a>.</p>
<p>I don’t want to slow this page down too much by embedding two videos, so here’s <a href="https://vimeo.com/283654933">a link to the CSS Day talk recording</a>.</p>
<p>You <em>may</em> want to watch the videos at a slower pace than the default. 🙈</p>
<p class="note update">
If you run an event and would like me to give this talk at it, please feel free to <a href="mailto:soueidan.sara@gmail.com">drop me a line</a> and request it.
</p>
<h3 id="the-talk-slides" tabindex="-1">The Talk Slides</h3>
<p>You can find the slides online <a href="https://sarasoueidan.com/slides/SVG-Filters-Crash-Course.pdf">here</a>.</p>
<div class="l-aspect-ratio wide">
<object width="100%" height="100%" type="application/pdf" data="https://sarasoueidan.com/slides/SVG-Filters-Crash-Course.pdf?#zoom=27&scrollbar=0&toolbar=0&navpanes=0" id="pdf_content">
</object>
</div>
<h3 id="further-resources" tabindex="-1">Further Resources</h3>
<p>I Googled a <em>lot</em> while learning about filters. I jumped between the <a href="https://www.w3.org/TR/filter-effects/">official specification</a> and a bunch of articles that were not specifically about SVG filters, but more about the concepts that you need to know about in order to understand SVG filters. So I learned about noise generation, turbulence, random math functions, color matrices, color functions, Gaussian and other blur operations, and so, so much more. But there were a few resources that I found myself coming back to, and those include:</p>
<ul>
<li>
<p><a href="https://webplatform.github.io/docs/svg/tutorials/smarter_svg_filters/">The Web Platform SVG Filter docs</a> were an indispensable resource.</p>
</li>
<li>
<p>I learned so much from <a href="https://twitter.com/mmullany">Michael Mullany</a>’s work. He wrote some of the content of the Web Platform docs, and has written <a href="https://www.creativebloq.com/netmag/how-go-beyond-basics-svg-filters-71412280">a wonderful introduction to SVG filters for net magazine</a> that I learned a tonne from.</p>
</li>
<li>
<p>Michael also has <a href="https://codepen.io/mullany/">a bunch of SVG filter experiments on Codepen</a> that I highy recommend checking out. I also broke down most of his pens and one of the demos in my talk was inspired by / borrowed from his examples.</p>
</li>
<li>
<p><a href="https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/">Dirk Weber’s wonderful experiments on Smashing Magazine</a>. The article doesn’t dive into the concepts needed or used in SVG filter primitives, but I broke down some of his demos as a way of learning more about the filters used.</p>
</li>
</ul>
<p>I learned everything else by picking up small tips and pieces of information from here and there and sewing them together.</p>
<h3 id="so-many-possibilities" tabindex="-1">So Many Possibilities</h3>
<p>I only scratched the surface of what is possible with SVG Filters. You can do <em>so</em> much with SVG Filters. As I showed in my talk, you can recreate many graphic editor capabilities using almost exactly the same steps in SVG using filter primitives. But there are also limitations. I was particularly interested in recreating <a href="https://www.youtube.com/watch?v=6BWA5pAATKQ">one particular Photoshop color glitch effect</a> when I was working on the demos for the talk, but I don’t think there’s a way to do that using SVG’s filter primitives. I could be wrong, though. After all, I only scratched the surface and stopped halfway through my research because I had to focus on client work. So if you know a way to do this, please feel free to share and <a href="https://twitter.com/SaraSoueidan">tweet at me</a>.</p>
<p>After my talk, many people expressed their interest in experimenting with SVG filters, and many of them already started sharing great demos on Codepen. <a href="https://twitter.com/yoksel_en">Yoksel</a> has been tweeting about her SVG filter experiments lately that I highly recommend checking out. Others have requested more resources after my talk, which prompted me to write this article today. So, I hope you too find it useful and are excited enough to experiment with SVG filters today.</p>
<p>Cheers,<br />
Sara</p>
Interview: net Magazine September 2018 Issue #310
2018-08-09T11:09:53Z
https://sarasoueidan.com/blog/net-interview-2018/
<p class="deck">
The September 2018 issue of <a href="https://www.creativebloq.com/net-magazine">net Magazine</a> came out today, featuring a six-page interview with yours truly, discussing all things front-end development and UX ahead of my upcoming <a href="https://sarasoueidan.com/workshops/universal-components/">workshop</a> at <a href="https://www.generateconf.com/london/">Generate Conference</a> in London.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/me/netmag-2018.jpg" alt="Screenshot of the netmag interview" />
<figcaption>
The interview features photos taken by me, in and outside of my home office.
</figcaption>
</figure>
<p>This isn’t my first interview feature in net magazine. My first feature was in 2015, a short while after I was <a href="https://sarasoueidan.com/blog/developer-of-the-year-2-15-net-award/">awarded the Developer of the Year award</a> in the 2015 <a href="https://thenetawards.com/">net awards</a>. But this interview is different, and I prefer it over the previous one for a a couple of reasons:</p>
<ol>
<li>
<p>The previous interview didn’t focus much on my work as a front-end developer, even though I would have liked it to. This interview, on the other hand, is <em>all about</em> my work at the intersection of front-end development and UX. I enjoyed talking about what I do, including ways I stay productive and healthy during my work.</p>
</li>
<li>
<p>This interview features photos that I took myself, as opposed to having a professional photographer take them. With the exception of a photo of me speaking at <a href="http://cssday.nl/">CSS Day</a> last June — which was taken by <a href="https://twitter.com/drewm">Drew McLellan</a> — all the photos were taken by me, using my camera, with the help of my faithful tripod and remote control combination. It was fun doing these self portrait photoshoots over the last few months.</p>
</li>
</ol>
<img src="https://sarasoueidan.com/assets/images/me/netmag-2018-1.jpg" alt="Screenshots from the netmag interview" />
<p>The issue is now out, and you can buy a physical copy of the magazine or a digital copy if you use the net magazine mobile app.</p>
<p>—Sara</p>
On Designing and Building Toggle Switches
2018-07-23T01:51:50Z
https://sarasoueidan.com/blog/toggle-switch-design/
<p class="deck">Yesterday I was working on creating the slides and accompanying demos for my upcoming <a href="http://www.webdirections.org/code/speakers/sara-soueidan.php">Web Directions Code talk</a> next week. One of the demos I’m creating is a basic proof of concept for a simple switch that is used to switch the theme of a UI from light to dark and vice versa. I liked, and was inspired, by the theme switch in the Medium app, shown below.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/Medium-theme-switch.jpg" alt="Screenshot of the Medium Theme Switcher" style="width: 100%" />
<figcaption>
The Medium app’s theme customizer is a simple popup panel that includes a simple switch for switching from light to dark mode and vice versa.
</figcaption>
</figure>
<p>The only difference is that I wanted my switch to explicitly state which theme is currently enabled, so instead of just enabling and disabling a dark theme like the Medium switch, I wanted the user to explicitly switch between Light and Dark options. There’s no particular reason for that other than personal preference. There are <a href="https://inclusive-components.design/a-theme-switcher/">other ways</a> to do this as well. How you design it is a personal preference, as long as it works and is easily understandable by users.</p>
<p>As always, I started thinking about how to mark this simple element up, ensuring accessibility is baked right into it from the start. So I started doing my homework and reading and learning all I can about this topic.</p>
<p>It was important for me to make sure this demo is accessible even if it’s just a quick proof of concept for a talk. First of all, because the code for the demo will be public, so I have a bigger responsibility for making sure it’s accessible, because I wouldn’t want to spread any inaccessible code around, especially if there’s a chance people might be using it somewhere else.</p>
<p>Another reason I wanted this to be good is that I’ll probably want to reuse it for other components for my upcoming <a href="https://sarasoueidan.com/workshops/universal-components/">front-end components workshop</a>.</p>
<h2 id="start-with-the-markup" tabindex="-1">Start with the markup</h2>
<p>As I mentioned above, I started thinking about how to mark this element up ensuring it is accessible to screen readers. That’s when I realized (and it was a “D’oh!” kinda moment) that function and markup depend on how I want the toggle to behave, <em>and</em> on how I want it to look.</p>
<p>It was pretty clear to me: the switch would allow a user to choose between a light theme and a dark theme, with the light theme being the default. It was at this moment that radio buttons came to my mind: two options with one of them checked, and only one option can be checked at a time; that makes a great use case for good old radio buttons.</p>
<p>I knew I would have to choose something different if I wanted the UI to look and behave differently. For example, if I wanted the UI to say “Enable/Disable Dark Mode”, then I wouldn’t need or want to use radio buttons, because I’d only have one option to deal with that could be <strong>switched on and off</strong> — <em>that</em> would be a great use case for a checkbox or a good old toggle <code><button></code>.</p>
<p><strong>Takeaways:</strong></p>
<ul>
<li>Style and function are interrelated; so it helps to think about these two simultaneously when designing for accessibility (and designing anything in general, really).</li>
<li><strong>Always, always start thinking about the markup and accessibility when building components</strong>, regardless of how small or simple they seem.</li>
</ul>
<h2 id="research" tabindex="-1">Research</h2>
<p>As always, I needed to back my theory and practice up with good research. So I started reading. My first go-to references are Heydon Pickering’s <a href="https://inclusive-components.design/">Inclusive Components</a> and <a href="https://a11yproject.com/">The A11y Project</a>.</p>
<p>As it turns out, Heydon had <a href="https://inclusive-components.design/toggle-button/">a fantastic article just about Toggle Buttons</a> which I learned a lot from, and my friend <a href="https://www.scottohara.me/">Scott O’Hara</a> had <a href="https://scottaohara.github.io/aria-switch-button/">an ARIA switch button</a> included in <a href="https://a11yproject.com/patterns">the Patterns section</a> of the A11y Project. So, naturally, I inspected the code for that button and read Heydon’s article to confirm if I’m on the right path. Thankfully, it turns out I was.</p>
<p><em>Before I move on it’s worth mentioning that this is <strong>not</strong> an article about how to create accessible toggle switches. Heydon’s article does a fantastic job covering that.</em></p>
<p>The main points I personally concluded from the above research are:</p>
<ol>
<li>
<p>There are different types of switches that seem to do similar things but are fundamentally different when it comes to markup and accessibility. Just because they are styled to look the same, doesn’t mean they <em>are</em> necessarily the same.</p>
</li>
<li>
<p>I need to think about how I want the UI to behave, look <em>and sound</em> when marking the switch up. Design and UX first, then code.</p>
</li>
<li>
<p><strong>A toggle switch can be used to switch between two separate options, or it can be used to switch one option on and off (or like enabling/disabling an option).</strong> This is where the implementation differences start to manifest.</p>
</li>
<li>
<p>If you’re switching between two separate options, using standard radio buttons makes sense. <strong>Radio buttons are used when you need your users to choose one of two or more options, so this is a perfect use case for them.</strong> They also have basic accessibility and keyboard tabbing baked right in. Just make sure you don’t break the accessibility of either of these in the HTML or using CSS.</p>
</li>
<li>
<p>If the purpose of the switch is to enable/disable a feature (or turn it on/off), there are other approaches for it:</p>
<ul>
<li>Using a checkbox to check/uncheck (enable/disable) that option.</li>
<li>Using a <code><button></code> that can have two states: pressed and not pressed. This approach requires the use of ARIA which in turn will require JavaScript to function properly for assistive technologies (the ARIA attribute values are updated on click with JavaScript). Read Heydon’t article for more details on this approach.</li>
<li>This approach means that you have only one option, which in turn means that the switch button has only one associated label, and would probably not look like a “double-switch” button anymore, <em>unless</em>:
<ul>
<li>The double-switch button (indicating on/off) states would have clear text indicating what the current active state is; so it would look like this example from Scott’s demo mentioned in the A11y project : <img src="https://sarasoueidan.com/assets/images/on-off-switch.png" alt="the On/OFF switch pattern by Scott O’Hara" style="vertical-align: top;" /></li>
</ul>
</li>
</ul>
</li>
</ol>
<p>For my use theme switcher, it was a clear choice: since the user has two options (dark and light), I was going to use radio buttons. <strong>I didn’t want it to say “turn dark mode on/off” — I wanted it to say “Enable light mode or enable dark mode”.</strong> The solution will be CSS and HTML only, require no JavaScript, and accessibility would be baked right in by default.</p>
<p><strong>Takeaway:</strong></p>
<ul>
<li>Have a clear vision of what the design of the component is supposed to do and not do. Base your markup and styles on that.</li>
</ul>
<div class="note update">
<p>
If you like this article you may love my Universal Components Workshop. I’m running this workshop at a couple of events in the Fall, so you may like to sign up for one of them.
</p>
<p>
I also run in-house workshops, so you can request this workshop (or another one) for your team.
</p>
<a href="https://sarasoueidan.com/workshops/">Learn More</a>
</div>
<h2 id="inspecting-code" tabindex="-1">Inspecting Code</h2>
<p>Now, after reading and inspecting in the above resources, I decided to see how other people are marking such a switch up. After all, I knew I’d find tonnes of examples of toggle switches that look and behave like this visually on <a href="https://codepen.io/">Codepen</a>, and it’s always great to see how others are doing it — maybe I’m missing on some cool techniques for styling that I can learn, or maybe I’m missing some important piece of information when it comes to the markup and accessibility.</p>
<p>Since I was working <em>in</em> Codepen on my own version of this switch (demo shared below), I thought I’d inspect Codepen’s own toggle switch: the switch that you use to choose whether you want the pen to be public or private.</p>
<p><strong>Quick takeaway:</strong></p>
<ul>
<li>It is always helpful and interesting to learn from other people’s work by inspecting their code (be that via the devtools or on Codepen).</li>
</ul>
<h2 id="the-codepen-privacy-switch" tabindex="-1">The Codepen Privacy Switch</h2>
<p>The Codepen switch exhibited some weird behavior that I had also noticed before but never felt curious enough to “debug”. The following video shows this behavior:</p>
<video width="320" height="240" controls="" style="width: 100%;">
<source src="https://sarasoueidan.com/videos/codepen-switch-bug.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<p>First, it’s worth mentioning that I was always confused by this switch. The red color vs green always gave out a weird feedback loop. If I make the pen private, the color turns green, and making it public makes it red. In my head, green meant more like “this pen is accessible by people, it’s open” and red was more like “this pen is closed, people are NOT allowed access”. But in Codepen terms, the colors symbolized other things. More specifically, the red stands for “PUBLIC ZONE = DANGER ZONE” or “BEWARE this pen is now PUBLIC which is NOT GOOD”. (Sorry, I’m being too dramatic, but purposefully.)</p>
<p>Then there was the buggy behavior demonstrated in the video above (where clicking on the same label changes the value of the switch to the other label’s value). I was curious to see what was causing it so, again, I fired up devtools and inspected the code.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/codepen-switch-code.png" alt="Screenshot of the devtools inspecting code for the Codepen switch" />
<figcaption>
The Codepen switch is marked up as a checkbox that has two labels.
</figcaption>
</figure>
<p>I found that the Codepen switch is one checkbox that appears to have two labels. That’s why clicking on both “Public” and “Private” multiple times would cause the switch to be turned on and off. In other words, this is why the visual feedback/behavior of the switch did not match the purpose of the switch or what it seemed to do.</p>
<p>I <a href="https://twitter.com/SaraSoueidan/status/1021068702110887937">tweeted about this bug</a>, <a href="https://twitter.com/SaraSoueidan/status/1021068905652121600">its possible cause</a>, and <a href="https://twitter.com/SaraSoueidan/status/1021069590628044800">suggested an alternative solution</a> that could fix it.</p>
<p>I started getting responses and opinions from other developers, which led to a very good and enlightening discussion that I thoroughly enjoyed (one of the few positive aspects of Twitter!).</p>
<p>The general consensus was that the switch needed fixing, of course. But how it would be fixed depends largely on what the Codepen team wants it to do:</p>
<h3 id="if-the-switch-toggle-is-supposed-to-be-an-enable%2Fdisable-button-for-the-%E2%80%9Cprivate%E2%80%9D-option" tabindex="-1">If the switch toggle is supposed to be an enable/disable button for the “Private” option</h3>
<p><strong>Then:</strong></p>
<ul>
<li>That would mean that the ON/OFF switch pattern mentioned in the previous section above would be the ideal solution.</li>
<li>This means that the “Public” label would be discarded, and only the “Private” label would be preserved, with a clear indication that the switch next to it turns this option on or off. Which brings me to another point:</li>
<li>The visual representation of the switch would either have to change completely or it should be tweaked:</li>
<li>If the switch button were to remain visually the same (i.e. preserve the double-swicth “move this toggle left and right” behavior), I’d suggest adding “On/Off” labels to it akin to what Scott did in his A11y project demo, because according to WCAG, you should not use color alone to convery information. The colors also need to have enough contrast if you were to use them. And, again, I’d reconsider the red and green colors as they may confuse someone else like they confused me for a while. (<a href="https://twitter.com/geare_d/status/1021253054165762048">Someone suggested</a> grey and green, because “The addition of color would more closely connotate the enabled state (no color vs has color as opposed to two separate colors)”.)</li>
<li>The underlying structure and markup for this case (one label only) could be:
<ul>
<li>checkbox, that can be checked or unchecked. I would suggest just using simple (fancy, maybe) checkbox styles for this behavior and ditching the whole double-switch style altogether.</li>
<li><code><button></code> with <code>aria-pressed</code> (and <code>aria-checked</code> if needed), where the pressed state would indicate the pen is private, and the unpressed state indicates it’s not private; i.e. it’s public. The style of the button would also change in this case, and the behavior would requir JavaScript to modify the value of the ARIA attributes on click.</li>
</ul>
</li>
</ul>
<h3 id="if-the-switch-toggle-is-supposed-to-explicitly-offer-and-enable-two-separate-options%3A-public-and-private" tabindex="-1">If the switch toggle is supposed to explicitly offer and enable two separate options: Public and Private</h3>
<p>Then using a couple of radio buttons would make much sense, in my opinion. Two radio buttons, each with its own label, announced to assisitive technologies as a couple of separate options of which the user should choose one, and perfectly accessible via keyboards, <em>and</em> has no additional ARIA or JS requirements to function. And sprinkle some CSS on it to create the “double-switch” button style if you want (seriously I don’t know what else to call it) and you’re all done. The markup and styles for such a solution might differ slightly between developers, but the essence of the code would be the same.</p>
<p>Which brings me to the live demo…</p>
<h2 id="live-example" tabindex="-1">Live Example</h2>
<p>This is my implementation of a two-options toggle switch. I created this <em>not</em> as a solution to the Codepen switch, but simply as a switch for my light/dark theme switcher that I’m using in my talk:</p>
<p data-height="300" data-theme-id="3617" data-slug-hash="jpBbrq" data-default-tab="result" data-user="SaraSoueidan" data-embed-version="2" data-pen-title="Accessible Option(/Toggle) Switch" class="codepen">See the Pen <a href="https://codepen.io/SaraSoueidan/pen/jpBbrq/">Accessible Option(/Toggle) Switch</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>My switch is going to live in the context of a larger set of form elements, that’s why I don’t have any fieldset or legend in the demo. And yes I know, I know… you could also tweak the code to <em>not</em> need the extra <code>span</code>s in the markup and use pseudo-elements instead, but I chose this approach anyway, just because I wanted to. Try to play with the code and uncomment some of the styles (especially try removing the spans to see how my switch looks like without them) to dissect the code and have a better idea of my choices and what I was trying to achieve.</p>
<p>I also added focus styles to ensure keyboard users can see where the focus is since I’ve covered my default radio buttons with the spans, and labels don’t get highlighted on focus by default. I used <code>:focus-visible</code> only at first (with polyfill) but it didn’t work as expected in Firefox and Safari, so I ended up adding <code>:focus</code> back again and using that instead. Also, the focus styles aren’t very pretty, I know, but this demo is just a proof of concept so it doesn’t need to be strikingly beautiful.</p>
<p>If you’d like to see another demo that also uses radio buttons but implements them differently, without spans and using pseudo-elements, check this Codepen by my friend Scott O’hara out:</p>
<p data-height="300" data-theme-id="3617" data-slug-hash="zLZwNv" data-default-tab="result" data-user="scottohara" data-embed-version="2" data-pen-title="Radio Toggle Switch" class="codepen">See the Pen <a href="https://codepen.io/scottohara/pen/zLZwNv/">Radio Toggle Switch</a> by Scott (<a href="https://codepen.io/scottohara">@scottohara</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<h2 id="final-words." tabindex="-1">Final Words.</h2>
<p>OK let me start by saying that I could be wrong. I know that the Codepen folks may want something completely different, or think of something completely different. So this article is not meant to redesign the Codepen switch button, but rather serve as a documentation of my research and train of thought while working on creating my own switch button for my talk. I’ll need to do even <em>more</em> research when it’s time to continue working on my new workshop, and things may change, and I know I’ll learn and know more by the time I create my next switch. But, until then, I know I’ve got this blog post to reference for some of the thoughts and ideas that crossed my mind when I took the first step into this.</p>
<p>I hope you find something useful reading this article. An if you’ve made it this far already: thank you for reading.</p>
<p>Cheers.</p>
A new Smashing talk. A smashing new experience.
2018-06-27T21:15:03Z
https://sarasoueidan.com/blog/smashing-talk/
<p>I’m currently in Toronto, Canada, where I ran a workshop two days ago and gave a talk today at yet another <a href="https://smashingconf.com/">SmashingConf</a> event. Like all previous SmashingConfs, it was… well… smashing! But this one stood out for me so much that, for the first time ever, I found myself firing my code editor up to write this short and sweet conference review. I felt like I needed to save a part of this experience in written format. I’ve never written conference reviews before, so this goes to say a lot about how much I’ve enjoyed this event.</p>
<h2 id="no-slides!" tabindex="-1">No Slides!</h2>
<p>This was the first SmashingConf event with a new twist: all talks are performed live, with no slides allowed. All speakers (myself included, of course) shared a small part of their everyday workflow in what I would consider the most engaging talk format so far.</p>
<h3 id="from-an-attendance-point-of-view%3A-i-think-this-was-the-best-conference-learning-experience." tabindex="-1">From an attendance point of view: I think this was the best conference learning experience.</h3>
<p>I have a very, very short attention span when it comes to conference talks. I usually lose my focus and get distracted about 5 to 10 minutes sitting and listening through a talk. It’s something I’ve tried to “fix” about my brain, but never could. Sitting there and watching people just talk and move through tonnes of slides with images and text on them has never been appealing enough for my brain.</p>
<p>But seeing people <em>do</em> things is a whole other thing. It’s like getting a glimpse into their real everyday work lives, and learning from them first-hand, without any barriers. <strong>I personally learn more from watching people than I do from listening to them.</strong> And this is why I absolutely loved this conference experience.</p>
<h3 id="from-a-speaking-point-of-view%3A-this-was-the-most-fun%2C-least-stressful-of-all-my-talks-so-far." tabindex="-1">From a speaking point of view: this was the most fun, least stressful of all my talks so far.</h3>
<p>I’ve done a bit of live-coding in my talks before, and I’ve felt more “at home” during those live coding sessions than all the rest of my speaking. There’s something about getting my hands busy on stage that distracts my brain enough to not even have a chance to wreck my nerves like it usually does.</p>
<p>I usually get very, very nervous before my talks. And no, it never got easier over time. If anything, it felt more like it was getting worse every time. Actually, ”nervous” is a very understated way to describe my situation before I speak. I get heart palpitations; I feel extremely dehydrated; and I feel very dizzy (sometimes I worry I might actually faint).</p>
<p>Maybe it’s because expectations start building up the more you speak, and you find yourself competing with your past self to do better every time. This is generally good for self growth, but expectations are very nerve-wrecking.</p>
<p>But this time… this time it was different! I think it may be because this talk format was, in essence, very similar to a workshop format. And having given many workshops in the last few years, it’s become kind of a second nature for me to do things live in front of people. Maybe? I don’t know.</p>
<p>I felt almost zero nervousness. I was too busy getting things done to feel nervous.</p>
<p>For me, showing people how to do things is so much easier than talking about it. Even though I sound like I got it all together, speaking in English is not as effortless as it may sound to me, and translating ideas that are runnig through your brain at the speed of light live is not easy. So working through concepts and techniques instead of talking about them was much more liberating for me.</p>
<p>I love the fact that this talk format also kind of rid me of the expectation that it needed to be flawless. Every speaker knows that doing things live on stage comes with a risk of failure. But since all speakers were in this together, it kind of helped relieve some of the pressure, and so instead of fretting over moments were they get stuck on stage, I saw speakers like Harry Roberts embrace these moments as a natural part of this talk format and even make fun of those moments, again adding more humor to an already interesting and engaging talk.</p>
<h2 id="but-it-isn%E2%80%99t-for-everyone%E2%80%A6" tabindex="-1">But it isn’t for everyone…</h2>
<p>I believe some attendees and some speakers were not very fond of this format. And that’s okay. Everyone learns and teaches in different ways, and I think it’s good to try new things out and learn more about what works for us and what doesn’t.</p>
<h2 id="there-were-great-people.-and-i-had-enlightening-conversations." tabindex="-1">There were great people. And I had enlightening conversations.</h2>
<p>I met some friends I’d only known over the internet for the last few years. I’ve re-met and had formidable conversations with wonderful friends and acquaintances, many of which were truly, truly life-changingly inspiring! (Believe me, this is not something I would say lightly.)</p>
<p>I saw some of my previous workshop attendees, who shared updates on how what they learnt in the workshop is currently helping them in their everyday work. It was very rewarding to hear this.</p>
<p>And then there’s the humbling fact that I shared the stage with some of my favorite people in the industry. It was truly great.</p>
<h2 id="toronto-is-beautiful!" tabindex="-1">Toronto is beautiful!</h2>
<p>Toronto is like a downsized, saner and more liveable version of NYC. And I <em>love</em> NYC. Toronto is kind of like the better version of it. I’m very much enjoying the city so far, and I look forward to a few more days here and more updates I’m going to share in another post next week.</p>
<p>While I’m here, I’m sharing my view of the city on <a href="https://instagram.com/SaraSoueidan">my Instagram</a>, where I’ve already posted quite a bunch of photos, if you’re into that kinda thing.</p>
<h2 id="i-would-definitely-do-it-again." tabindex="-1">I would definitely do it again.</h2>
<p>I do want to give slideless talks again. It could be at another SmashingConf, and it could be anywhere else. I think that, for my kind of talks, there’s much more value in demonstrating things live than there is in theoretical talks that don’t include practice. So I see this format as an improvement to the “normal” format for my talks. Of course, the live format wouldn’t be ideal for <em>all</em> my upcoming talks, and that’s also okay.</p>
<h2 id="smashing-is-smashing." tabindex="-1">Smashing is smashing.</h2>
<p>SmashingConf is one my favorite events, and the SmashingConf team and crew have become more like family over the years. I’m really happy I got to be a part of this particular event. Kudos and many thanks to Vitaly, Markus, Amanda and everybody else in the team that made this event possible, and for pushing the boundaries once again and challenging us to get out of our comfort zones. If you ever get a chance to attend a SmashingConf, I would recommend not missing it.</p>
Going Offline
2018-04-23T15:37:23Z
https://sarasoueidan.com/blog/going-offline/
<p class="deck">Earlier this month I rolled out a new and long overdue feature on this Web site: offline viewing. In other words, from now on, after your first visit, you can re-visit my Web site even when you’re not connected to the Internet. Furthermore, depending on the device and application you’re using to visit this site, you may also be able to add the site to your mobile homescreen, similar to other native applications.</p>
<h3 id="offline-with-service-workers">Offline with Service Workers</h3>
<p>Most of the main pages of the site (those linked in the menu in the header and links in the footer) will be saved for offline viewing by default on your first visit. Posts published in the Articles section (like this one) or in the Desk section will be available for offline reading on demand.</p>
<p>All this is possible thanks to the power of Service Workers.</p>
<h4 id="progressively-enhanced-experience" tabindex="-1">Progressively Enhanced Experience</h4>
<p>If you visit my site using a modern <a href="https://caniuse.com/#feat=serviceworkers">browser that supports Service Workers</a>, you will get an enhanced experience, with a colorful <i>“Save this page for offline reading”</i> button at the top of each post, that does exactly what it says: it caches the page offline so that you can view and read it again when you’re offline.</p>
<p>Using a Service Worker to save pages offline also enhances the overall experience of the site by making it load even faster than it already did before.</p>
<p>If a browser does not support Service workers, it will miss on these new features, but the general experience of the site will remain unchanged from what is was before — nothing will break. This is because all the features that Service Worker offers are only enabled when support for Service Workers is detected and the Worker is registered and installed.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// a Service Worker is registered only when it is supported</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'../../serviceworker.js'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">scope</span><span class="token operator">:</span> <span class="token string">'/'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="%E2%80%9Cgoing-offline%E2%80%9D" tabindex="-1">“Going Offline”</h3>
<p>I wrote my first Service Worker script about 3 or 4 years ago — the year Workers were announced and started getting implemented. But my Worker never saw the day of light. I remember getting caught up with client work and adding the script to my todo list for this Web site. I never got to finish it.</p>
<p>This month, <a href="https://abookapart.com/">A Book Apart</a> announced (and released early previews of) a new book titled “Going Offline”, written by none other than <a href="https://adactio.com/">Jeremy Keith (“adactio”)</a> himself. Like any book written by one of my favorite people in the industry, I leaped on the chance to read the early preview.</p>
<p>Over the course of two days, I read the book and followed along with it as I rewrote my old Service Worker script for the site. By the time I reached the end of the book, I had a Service Worker installed and working on the site, <em>and</em> I had added a Web App Manifest file — the file that enables your Web site or Web application to behave and look similar to a native application on your mobile device(s).</p>
<p>Jeremy covers literally everything you need to know to write and install your first Service Worker, tweak it to your site’s needs, and then write the Web App Manifest file to complete the offline experience, all in a ridiculously easy to follow style. It doesn’t matter if you’re a designer, a junior developer or an experienced engineer — this book is perfect for <em>anyone</em> who wants to learn about Service Workers and take their Web application to a whole new level.</p>
<p>If you care about the user experience of your site / application, making sure it loads blazing fast and works offline might be one of the best steps you could take to improve that experience for your users. And this book is a vital one if you’re not sure where to start doing that. I highly recommend it. I read the book over the course of two days, but it can easily be read in half a day. And as someone who rarely ever reads a book cover to cover (I tend to quit halfway through most books), this says a lot about how good it is. The book is now available for (pre)order <a href="https://abookapart.com/products/going-offline">here</a>.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>If you’re interested, you can take a look at my script <a href="https://sarasoueidan.com/serviceworker.js">here</a>. It is a basic script, and you can write an exact same script if you follow along Jeremy’s book. But of course it’s important, in my opinion, that you know, understand and customize what the script does to your own needs. Jeremy’s book helps tremendously with that. The book also contains a list of extra resources for further learning about Service Workers. So have a look at those if you need more information.</p>
<p>I have more work to be done on this site, and I’ll be writing about new features as they roll out. I’m quite excited about what’s coming next!</p>
<p>Thank you for reading!</p>
Case Study: Optimizing SVG Text & Image Delivery with Inline SVG
2018-01-10T16:55:20Z
https://sarasoueidan.com/blog/optimizing-svg-delivery-with-svg/
<p class="deck">
I love when I’m pushed to think of creative techniques when tackling design and dev challenges on my client projects. And it so happens that <a href="https://smashingmagazine.com/">the new Smashing Magazine design</a> released this year was one of the more (fun and) challenging projects I’ve worked on. And one of the challenges I had to tackle was delivering three fairly complex SVG images in a performant, accessible way.
</p>
<p>
</p>
<p>I was the front-end developer (and, at times, the designer) on the project. I’ve written <a href="https://sarasoueidan.com/case-studies/smashing-magazine/">a case study</a> about the project earlier this year. But the case study was more about the design aspect of the project than it was about the development of it. I’ve talked about the development part <a href="https://sarasoueidan.com/blog/optimizing-svg-delivery-with-svg/#smashing-case-study-videos" id="smashing-case-study-videos-ref" aria-describedby="footnote-label">at a few conferences during the last year</a>. This is the first in a series of articles going over some of the tips and tricks I mentioned in those talks.</p>
<h3 id="the-challenge" tabindex="-1">The Challenge</h3>
<p>The new Smashing Magazine offers, as a replacement to ads on the site, three new types of memberships. <a href="https://www.smashingmagazine.com/membership">The Membership signup page</a> shows these three membership options, with a fairly complex SVG illustration for each one.</p>
<p>The following image shows the three illustrations I worked on. Even though the content of these illustrations has changed since I worked on the project, the complexity of the images as well as the technique used to embed them remained unchanged.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/smashing-membership-illustrations.png" alt="The (old) Smashing membership illustrations." />
<figcaption>
The (old) Smashing membership illustrations.
</figcaption>
</figure>
<p>Each of these illustrations contains the title/name of a membership. There are no actual titles in the HTML to represent these memberships otherwise, which means that the text in each image <em>is</em> the title of that particular membership. This, in turn, means that <strong>this text needs to be as searchable, selectable and accessible as real (HTML) text</strong>.</p>
<p>In other words, the text inside the images needs to remain real text and not be converted to outlines, for starters. SVG text — wrapped in <code><text></code> elements within an <code><svg></code> — is searchable, selectable and accessible by default. So we have a great start there.</p>
<p>And since the text inside each image is real text, it needed to be styled like the rest of the text — more specifically, the headlines — on the site.</p>
<p>Using <code>font-family</code>, you can apply a font face to SVG text the same way you would apply a font face to text in HTML. And I wanted the font styles to be applied in the main style sheet, not in a seperate style sheet referenced within each SVG image.</p>
<p>So, ideally, these three images would be embedded inline in the page. This would give us both the styling from the main style sheet as well as flexibility to do pretty much anything else we may want to do with the text otherwise.</p>
<p>But inlining the illustrations was not as simple as it sounded. Each illustration was complex enough to make the size of it so large that adding it inline to the HTML was simply not an option. The following recording shows the size of <em>one</em> of these illustrations.</p>
<img src="https://sarasoueidan.com/assets/images/smashing-membership-illustration.gif" alt="" />
<p>Inlining three times the amount of code shown in the above image was definitely not an option. But I still wanted the text inside those images to be inlined. So, what’s the best way to get the best out of SVG: searchable, selectable and accessible text, a cacheable illustration (hence better performance), and clean code?</p>
<h3 id="the-solution" tabindex="-1">The Solution</h3>
<p>There are seven different ways to embed an SVG on a Web page. Choosing which one to use depends largely on the case at hand and what your project requirements are. But something that I frequently mention when talking about embedding techniques is that you may not have to choose <em>one</em> technique. In other words, <strong>you can choose to embed an SVG using more than one embedding technique at the same time!</strong></p>
<p>SVG is a document format, not just an image format. Thus, referencing external images inside an <code>svg</code> document is possible just like it is in HTML. So, why not take this a step further and reference <em>an SVG</em> image from within an SVG document?</p>
<p>Since I wanted the text to be inlined and the rest of the image to be external and cacheable, and since the nature of SVG allows me to do pretty much anything with its contents, I thought “Why not split each of these images into separate parts: text and illustration?”, and then use an inline <code><svg></code> to wrap and stitch these two parts together?</p>
<p>In other words, an inline <code><svg></code> in the page would contain the <code><text></code> of the illustration, and then I’d reference the rest of the the illustration using an <code><image></code> element, within the same inline <code>svg</code>. So, in essence, I’d be working with SVG content in a similar way to HTML text and images. We use an <code><svg></code> document to embed an SVG image plus its accompanying text. Then, using some tweaking, I’d position the inline text on top of the illustration so that the end result would look like the original image.</p>
<p>So, I took each of the three images and removed the text content from them. That left me with three illustrations with no titles.</p>
<p>Then, for each illustration, I embedded that illustration within an inline <code><svg></code> that also contained the text for that illustration. The code looked something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>318.029<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>267.921<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 318.029 267.921<span class="token punctuation">"</span></span> <span class="token attr-name">aria-describedby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Supporter Plan<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/images/plan--supporter--no-text.svg<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>image</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <text x=173" y="50" font-size=".8em">Supporter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>text</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using this solution, I achieved all three main goals I needed:</p>
<ol>
<li>The complex illustration is no longer embedded in the HTML. This means <strong>the HTML code remains clean and uncluttered.</strong></li>
<li><strong>The illustration is cached upon subsequent requests</strong>, which is a win for performance.</li>
<li>The title for each plan is real, inline text, and is therefore <strong>searchable, selectable and accessible</strong>.</li>
</ol>
<p>Then, to finish this off, the text inside the SVG representing the title for the respective plan was styled further in CSS:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">svg text</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">text-transform</span><span class="token punctuation">:</span> uppercase<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> $mija<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And… that’s pretty much it.</p>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>Replacing the old illustrations with new ones was easy and fast. I got three new illustrations without text in them, and referenced those by simply changing the path to the images in the inline <code><svg></code>s, and then tweaked the <code>x</code> and <code>y</code> for the <code><text></code> of each one to position it correctly in the new images. The styles for the text and everything else was left untouched.</p>
<p><strong>Sometimes you don’t have to choose just <em>one</em> embedding technique to embed an SVG.</strong> Keeping the document nature of SVG in mind opens doors to creative solutions that might otherwise not come to mind at first glance. SVG is, after all, both a document and an image, and that’s where most of its power really comes from.</p>
<p>I hope you found this tip useful. Don’t forget to subscribe to the RSS (link in footer below) to receive the latest and new articles in your RSS reader as soon as they’re out.</p>
<p>Thank you for reading.</p>
<div class="footnotes">
<h4 id="footnotes__title">Footnotes</h4>
<ol class="footnotes__list">
<li class="footnotes__item" id="smashing-case-study-videos">
<p>
There is <a href="https://vimeo.com/214427831">a video recording</a> of me going over ths particular trick if you're more into listening than reading.
<a href="https://sarasoueidan.com/blog/optimizing-svg-delivery-with-svg/#smashing-case-study-videos-ref" aria-label="Back to content">↵</a>
</p>
</li>
</ol>
</div>
Cropping & Scaling Images Using SVG Documents
2018-01-02T04:07:56Z
https://sarasoueidan.com/blog/svg-object-fit/
<p class="deck">
I’m always interested in and thinking about ways to use SVG in my client projects to solve common UI challenges — uses that go beyond simple icon display and animated illustrations. I’m also always researching and looking for practical uses of SVG to add to my <a href="https://sarasoueidan.com/speaking/">talk</a> and <a href="https://sarasoueidan.com/workshops/">workshop material</a> that go beyond SVG the image format, and more into the document nature of it. After all, SVG on the Web isn’t just about displaying pretty and animated illustrations.
</p>
<p>
I’m also particularly interested in CSS and SVG as a combination (that I like to call “The Power Combo”) to solve common real-world challenges. These two work <em>really</em> well together. And it so happens than some of what’s possible in CSS is also possible in SVG, either because the CSS functionlity was imported from SVG to begin with, or simply because SVG documents also happen to offer tools that achieve the same functionality. And since SVG goes way back and has much better support than newer CSS features, it is possible to use SVG either as a fallback or as a replacement to some CSS functionalities. It all depends on the browser support you’re after.
</p>
<p>SVG comes with a pair of attributes — namely <code>viewBox</code> and <code>preserveAspectRatio</code> — that allow us to manipulate the contents of an SVG (whether vector content or raster images) in a myriad of ways to achieve a myriad of things. One thing we can do with these attributes is control the scaling and position of the contents of the SVG. In this article, we’ll use these attributes to crop, scale and position images, as a fallback or alternative to the CSS <code>object-fit</code> and <code>object-position</code> properties.</p>
<div class="note">
If you’re not familiar with the SVG <code>viewBox</code>, viewport and <code>preserveAspectRatio</code>, I highly recommend reading <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">my extensive guide</a> on the subject. Although this article does not require a deep understanding of these attributes, I highly recommend you get comfortable using these attributes.
</div>
<h3 id="cropping%2C-scaling-and-positioning-in-css-with-object-fit-and-object-position" tabindex="-1">Cropping, Scaling and Positioning in CSS with <code>object-fit</code> and <code>object-position</code></h3>
<p>Say you’re using a CMS, and you’re allowing your users or authors to upload photos of themselves to use as an avatar next to their biography on the site. And you want to make it possible for them to upload any image of any size and aspect ratio, and then you’d handle the cropping of that image before you display it on the page in a box that might very well have a different aspect ratio than the image the author uploaded.</p>
<p>There are many ways you could handle this. For example, you can handle the image cropping on the server-side using PHP or some JavaScript script, and then serve the cropped image on the site. You may even be in a different scenario, where you just want to be able to quickly crop and display the images on a page, without using a CMS and back-end script.</p>
<p>Fortunately, today, CSS has two properties that make cropping and scaling images within a fitted box a breeze. These properties are <code>object-fit</code> and <code>object-position</code>.</p>
<p>The <code>object-fit</code> property specifies how the contents of a <a aria-describedby="footnote-label" href="https://sarasoueidan.com/blog/svg-object-fit/#replaced-element" id="replaced-element-ref">replaced element</a> should be fitted to the box established by its used height and width.</p>
<p>Even though a bitmap image has its own intrinsic dimensions and aspect ratio, you can size it to fit into any box of any size as defined in your CSS. And you can choose whether you want to preserve the aspect ratio of the image or not, all using one property (<code>object-fit</code>) and one line of CSS.</p>
<p>The following image shows the effect of each of the possible values for <code>object-fit</code>:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/object-fit-values.png" alt="object-fit values" />
<figcaption>
The result of applying the different <code>object-fit</code> values to an image to be fitted in a box with a different aspect ratio.
</figcaption>
</figure>
<p>By default, the image is centered within its containing box (the square, in our example). You can change that default position using <code>object-position</code>, which takes values similar to the values of <code>background-position</code>. For example, <code>object-position: top left</code> will align the top edge of the image to the top border of the box, and the left edge of the image to the left border of the box. Here’s a live Codepen for you to try the effect of changing <code>object-position</code> on the images:</p>
<p data-height="1000" data-theme-id="3617" data-slug-hash="31c6225244914f3967b3f7c773394c6c" data-default-tab="result" data-user="SaraSoueidan" data-embed-version="2" data-pen-title="CSS `object-fit` Values" class="codepen">See the Pen <a href="https://codepen.io/SaraSoueidan/pen/31c6225244914f3967b3f7c773394c6c/">CSS `object-fit` Values</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>Browser support for <code>object-fit</code> and <code>object-position</code> is very good: it is supported in all the latest browsers, including MS Edge 16+ and Opera Mini, though it requires the <code>-o-</code> prefix in the latter. You can see the latest updated browser support <a href="https://caniuse.com/#feat=object-fit">on CanIUse.com</a>.</p>
<p>If you, like me, want to be able to provide a similar experience to Internet Explorer, you’re going to need an alternative solution, or at least a fallback. And, ideally, the alternative solution needs to provide support at least back to IE9, maybe? This is where SVG can fill in.</p>
<h3 id="cropping-%26-scaling-images-with-svg" tabindex="-1">Cropping & Scaling Images with SVG</h3>
<p>If you’ve ever played with the SVG <code>viewBox</code>, then you know that the coordinate system defined by the <code>viewBox</code> does not necessarily need to have the same aspect ratio as <a href="https://sarasoueidan.com/blog/svg-object-fit/#viewport-coordinate-system" id="viewport-coordinate-system-ref" aria-describedby="footnote-label">that of the <code><svg></code> viewport</a>.</p>
<p>And when the aspect ratio of the <code>viewBox</code> is not the same as that of the viewport, the browser needs to position the former in the latter similar to the way the photo was being positioned inside the box in the previous section.</p>
<p>By default, just like with <code>object-fit</code>, the browser will fit the <code>viewBox</code> inside of the SVG viewport (or “box”) by <em>containing</em> it inside of it, such that the entire <code>viewBox</code> — and, thus, all the contents of the SVG — are visible inside the viewport.</p>
<p>Using the <code>preserveAspectRatio</code> attribute, you can change the position and scale of the <code>viewBox</code> — and, thus, all the contents of the SVG — similar to the way <code>object-position</code> changes the position and scale of the image inside the box when using <code>object-fit</code>.</p>
<p>For example, suppose we have a square svg (aspect ratio 1:1) and a <code>viewBox</code> that has a different aspect ratio (2:1). The easiest and fastest way to visualize the <code>viewBox</code> coordinate system in the svg is to create a <code><rect></code> that <a href="https://sarasoueidan.com/blog/svg-object-fit/#viewbox-origin" id="viewbox-origin-ref" aria-describedby="footnote-label">starts at the coordinate system’s origin</a> and has a width and height value of <code>100%</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 500 250<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- This rect is the same size of the viewBox --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#FEDA00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The result of the above code is shown in the image below. The yellow rectangle represent the size and position of the <code>viewBox</code> within the svg viewport.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/viewbox-vs-viewport-aspect-ratio.png" alt="Screenshot of an SVG containing a viewBox and rect with different aspect ratio." />
<figcaption>
The result of applying the different <code>object-fit</code> values to an image to be fitted in a box with a different aspect ratio.
</figcaption>
</figure>
<p>Now, using <code>preserveAspectRatio</code>, you can change the position of the viewBox as well as its size (or scale) within the viewport similar to the same way we could change the position and scale of our image in the previous section using <code>object-fit</code> and <code>object-position</code>.</p>
<hr />
<p>A <code>preserveAspectRatio</code> value is made up of two keywords, one of them represents the scale <code>viewBox</code> and has one of two values: <code>meet</code> or <code>slice</code>.</p>
<p><code>meet</code> has the same effect as <code>object-fit: contain;</code> (or <code>background-size: contain;</code>) and <code>slice</code> has the same effect as <code>object-fit: cover;</code> (or <code>background-size: cover;</code>). The former will preserve the aspect ratio of the viewBox and fit it inside the viewport so that it’s entirely visible. This is the default behavior. Whereas <code>slice</code> will scale the viewBox up, while preserving its aspect ratio, so that it covers the entire viewport area, even if it means cutting some of the content off (hence the “slicing” effect).</p>
<p>The other keyword in <code>preserveAspectRatio</code> represents and controls the position of the <code>viewBox</code> within the viewport. It has 19 values, including <code>none</code> which tells the browser to scale the <code>viewBox</code> to fill the viewport area without preserving its aspect ratio, and is similar in effect to <code>object-fit: fill;</code>.</p>
<p>The default value for the <code>preserveAspectRatio</code> is <code>xMidYMid meet</code>, which is the value the browser uses even if you completely omit the attribute from the <code><svg></code>.</p>
<p>The following snippet using <code>preserveAspectRatio="xMinYMin meet</code> will change the position of the <code>viewBox</code> in the previous example such that the yellow rectangle’s top edge is aligned with the viewport’s top edge, and its left edge is aligned with the left edge of the viewport, while keeping the whole rectangle contained within the viewport and preserving its aspect ratio. <code>xMinYMin</code> is equivalent to <code>0% 0%</code> or <code>left top</code> values in <code>background-position</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 500 250<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMinYMin meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- This rect is the same size of the viewBox --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#FEDA00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<div class="note">
I’ve created <a href="http://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/">an interactive demo</a> that includes a cheatsheet that maps each of the <code>preserveAspectRatio</code> values to one of <code>background-position</code> values. I highly recommend checking it out.
</div>
<p>Now, to get back to the objective of this article. If you replace the <code><rect></code> element with an image (such as a photograph) and you match the aspect ratio of that image with that of the <code>viewBox</code>, the browser’s default behavior will be to position the <code>viewBox</code> (and consequently also the image) so that it is fully contained and centered within the viewport (assuming again that the viewport’s aspect ratio is different from that of the <code>viewBox</code> and the image).</p>
<p>The following Codepen shows that in action. We have a 1:1 aspect ratio SVG, and an image with dimensions 579px by 375px. I’m using the same as the one I used in the CSS demo in the previous section again, and I’m keeping the default <code>preserveAspectRatio</code>. Try changing the value of <code>preserveAspectRatio</code> to see how the changes affect the position and scale of the image within the SVG.</p>
<p>To get the effect of <code>object-fit: cover;</code>, for example, you need only change <code>meet</code> into <code>slice</code> — the image will remain centered by default with <code>xMidYMid</code>.</p>
<p data-height="450" data-theme-id="3617" data-slug-hash="b47336d56a318d056218aa57e8889f3a" data-default-tab="html,result" data-user="SaraSoueidan" data-embed-version="2" data-pen-title="b47336d56a318d056218aa57e8889f3a" class="codepen">See the Pen <a href="https://codepen.io/SaraSoueidan/pen/b47336d56a318d056218aa57e8889f3a/">b47336d56a318d056218aa57e8889f3a</a> by Sara Soueidan (<a href="https://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>Pretty much any combination of values of <code>object-fit</code> and <code>object-position</code> can be replicated using <code>preserveAspectRatio</code> on <code>viewBox</code>. And what’s best is that this technique will work in any and all browsers that support SVG, which means that it will work in Internet Explorer all the way back to IE9. IE8 does not support SVG so you’d have to provide a different solution for it if you need to support it.</p>
<h3 id="one-more-thing%3A-making-the-svg-solution-more-accessible." tabindex="-1">One more thing: making the SVG solution more accessible.</h3>
<p>One thing that the SVG solution is missing at this point is an alternative to the <code>alt</code> attribute, because an image should <em>always</em> have that. If the image is just decoration, the <code>alt</code> attribute can be left empty, but it should never be omitted.</p>
<p>To make the SVG snippet accessible, you can add the SVG alternative to <code>alt</code>: the <code><title></code> element, first thing in the SVG, before your <code><image></code> declaration. So, doing that in the example above, the code would look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 579 375<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMid meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Painter’s Hands<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://s3-us-west-2.amazonaws.com/s.cdpn.io/9674/photo-1501366062246-723b4d3e4eb6.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>image</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Last but not least, to <a href="https://developer.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/">enhance the accessibility of SVG</a> further, add a couple of attributes on the <code><svg></code> element to describe the role of the <code><svg></code> as well as re-enforce the relationship between the <code><svg></code> and the <code><title></code> element, so that the latter is recognised by screen readers as the accessible name for the SVG content (—our image, in this case).</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 579 375<span class="token punctuation">"</span></span> <br /><span class="highlight-line"> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMid meet<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span> <span class="token attr-name">aria-role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Painter’s Hands<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://s3-us-west-2.amazonaws.com/s.cdpn.io/9674/photo-1501366062246-723b4d3e4eb6.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>image</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>And just like that, you now have a perfectly accessible SVG alternative to a CSS <code>object-fit</code> declaration.</p>
<h3 id="summary-%26-recap" tabindex="-1">Summary & Recap</h3>
<p>You can crop and scale any image using CSS <code>object-fit</code> and <code>object-position</code>. However, these properties are only supported in the latest version of ME Edge as well as all other modern browsers.</p>
<p>If you need to crop and scale an image in Internet Explorer and provide support back to IE9, you can do that by wrapping the image in an <code><svg></code>, and using the <code>viewBox</code> and <code>preserveAspectRatio</code> attributes to do what <code>object-fit</code> and <code>object-position</code> do.</p>
<p>The snippet can replace an <code>object-fit</code> declaration:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>y<span class="token punctuation">"</span></span> <br /><span class="highlight-line"> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 imgX imgY<span class="token punctuation">"</span></span> </span><br /> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><align> <meetOrSlice>”<br /> aria-labelledby=<span class="token punctuation">"</span></span><span class="token attr-name">title"</span> <span class="token attr-name">aria-role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> img alt here <span class="token entity named-entity" title="&ly;">&ly;</span>/title></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“100%”</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>image</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>where <code>imgX</code> and <code>imgY</code> are the dimensions of the image you want to crop and scale, and <code><align></code> and <code><meetOrslice></code> are the two keywords that determine the scale and position of the image within the SVG wrapper.</p>
<p>And that’s it. A simple SVG tip to provide better cross-browser support for a less-supported CSS feature.</p>
<p>I hope you like this tip and find it useful. Thank you for reading!</p>
<div class="footnotes">
<h4 id="footnotes__title">Footnotes</h4>
<ol class="footnotes__list">
<li class="footnotes__item" id="replaced-element">
<p>
A replaced element is an element whose dimensions and content are defined outside the scope of CSS. For example, a bitmap image has an intrinsic width and an intrinsic height specified in absolute units, and from which the intrinsic ratio can be determined. Dudley Storey puts it nicely when he says that ‘another way of thinking of replaced elements is “any tag that has its content replaced by an outside source”. <code><img></code> and <code><video></code> are obvious examples’.
<a href="https://sarasoueidan.com/blog/svg-object-fit/#replaced-element-ref" aria-label="Back to content">↵</a>
</p>
</li>
<li class="footnotes__item" id="viewport-coordinate-system">
<p>
<a href="https://sarasoueidan.com/blog/svg-object-fit/#viewport-coordinate-system-ref" aria-label="Back to content">↵</a>
</p>
</li>
<li class="footnotes__item" id="viewbox-origin">
<p>
If the origin of the <code>viewBox</code> is changed, the origin of the <code>rect</code> should be changed to match that, otherwise the rectangle can no longer be used as a visualization of the current user coordinate system in use.
<a href="https://sarasoueidan.com/blog/svg-object-fit/#viewbox-origin-ref" aria-label="Back to content">↵</a>
</p>
</li>
</ol>
</div>
Auto-Sizing Columns in CSS Grid: auto-fill vs auto-fit
2017-12-29T00:00:00Z
https://sarasoueidan.com/blog/auto-fill-auto-fit/
<p class="deck">
In this article I explain the subtle but important difference between <code>auto-fill</code> and <code>auto-fit</code> for sizing columns in CSS Grid. Each of them exhibits a responsive behavior once compbined with <code>repeat()</code> that you may or may not want for your responsive layout.
</p>
Migrating from Jekyll+Github Pages to Hugo+Netlify
2017-06-06T00:00:00Z
https://sarasoueidan.com/blog/jekyll-ghpages-to-hugo-netlify/
<p class="deck">
During the last 18 months, working on my Web site became a daunting task—be that for developing, redesigning it, writing a blog post, or making updates to my speaking and workshop pages. My then static site generator, <a href="https://jekyllrb.com/">Jekyll</a>, is why. And a change has long been overdue...
</p>
<p>Jekyll became unbearably slow at compiling my Web site after every change I made. Until, at one point, <strong>waiting for the site to compile became a torturous, life-sucking process that I wanted to avoid at all costs.</strong></p>
<p>This may sound exaggerated, but I promise you it’s not. Jekyll became way, way too slow. “Too slow” is actually an understatement. Recently, every time I changed a CSS property or made any change in the HTML <strong>I had to wait up to five minutes for that change to be picked up and compiled by Jekyll</strong>. I am, once more, <em>not</em> exaggerating. Jekyll used to literally just… freeze. I’d have to ctrl+C my way out of the freeze and then run it again for it to pick up the changes and finally compile. And if I made many changes in a row, my Macbook would heat up so much and the fan would go so crazy that it sounded like an airplane about to take off.</p>
<p>My site is relatively small, I’d say. I have less than 100 blog posts. Less than 60 at the time of writing of this article, actually. And only a few static pages. I don’t use heavy JavaScript. In fact, I barely need to use <em>any</em> JavaScript. And yet, Jekyll still choked every time it had to compile it.</p>
<p>Yes, I did use Jekyll flags such as <code>--incremental</code> and every single other flag and setting that I found or that someone recommended to speed up the compiling process. But no, it did not help.</p>
<p>I can’t even emphasize how bad it got during the last year. I would literally feel the stress hormones increase in my blood stream every time I so much as thought about making a change to my Web site. I knew I would be about to give myself one hell of a bad time doing so.</p>
<p>But I knew that this couldn’t go on forever. I knew I’d have to ditch Jekyll and migrate to a new generator at <em>some</em> point. I just never found the time to do so. Actually, to be more honest here, I never <em>made</em> the time for it because every time I had time off a project I wanted to make the most out of that time by staying <em>away</em> from my computer. My site was just not a priority, especially since I’d been very much still undecided about <em>what</em> to use as an alternative. So I kept stalling.</p>
<p>But recently, knowing I had a couple of weeks off to do practically anything, and since I’ve been having a <em>lot</em> of ideas for my blog that started piling up and that I really want to get out there, such as a setting up a newsletter, tweaking the design, improving the code (which is still WIP), adding a new type of content section (coming soon) and a few more ideas, I finally managed to put my head into it and do it, because I <em>want</em> to get my ideas out, and write a few blog posts. <strong>But I needed to start enjoying working on my Web site again, first.</strong> So I finally thought to myself: <em>“That’s it. I’m just gonna have to put my head down for a few days this week and dedicate my time to moving to the new static generator”.</em> I knew this was a necessary and extremely useful time investment that I just had to finally make. I put my mind to it, and just did it. (This is the most effective way to be productive, really: Just do it.)</p>
<h3 id="choosing-a-static-site-generator" tabindex="-1">Choosing a static site generator</h3>
<p>As I mentioned earlier, one of the reasons I didn’t make the switch earlier to another generator was because I didn’t know which one I wanted to use. Several Twitter friends suggested a few of the many available options. But I never felt comfortable with any of them. You see, everyone has some way that their brains work, and some way they like to organize their files, directories, and work, that works for them. None of the static generators I saw gave me everything I wanted and needed for my site. Until someone once suggested having a look at <a href="https://gohugo.io/">Hugo</a>.</p>
<p>I took a few minutes to read <a href="http://gohugo.io/overview/introduction/">the docs</a>, just to get an idea of what to expect and what Hugo had to offer—to get a first impression of it, so to speak. After reading a little into <a href="http://gohugo.io/content/organization/">the content structure and organization</a> section and learning how Hugo offers the ability to create many different content categories and sections, plus all the general flexibility it provides, I thought that this was the static site generator I’d always wanted and needed. The organization and structure looked exactly like what I had imagined my own site to have.</p>
<p>But what made me settle for Hugo out of all other options was seeing <a href="https://novelist.xyz/tech/hugo-vs-jekyll-static-site-generator/">how incredibly fast it is</a> compared to Jekyll. Not only has every single blog post I read online <a href="https://novelist.xyz/tech/hugo-vs-jekyll-static-site-generator/">made a comparison</a> and proved this, but I also got to experience this speed first hand while working on <a href="https://sarasoueidan.com/case-studies/smashing-magazine/">the Smashing Magazine redesign</a>.</p>
<p>The new Smashing Magazine (currently at <a href="https://next.smashingmagazine.com/">next.smashingmagazine.com</a>) uses Hugo as a static site generator. The setup that I got to use while building the front-end of the magazine was so blazingly fast that I had absolutely no doubt that the results I was reading about were true. And since my site is much smaller than Smashing Magazine, I knew I had nothing more to worry about. I mean, if Smashing Magazine can be compiled so ridiculously fast, how could my blog not be?</p>
<p class="note">
Please note that this is in no way meant to be a comprehensive guide to Hugo. There are still some bits and pieces that I am figuring out myself, so I'm in no position to write a comprehensive guide yet. You will find that you will also need to read through the Hugo docs for more details on the topics I’m going to be talking about. Think of this post as a helper that can help guide you where to start (and sometimes what to do) for certain particular Hugo topics. And finally, this is not a comparison post between Hugo and Jekyll. This is more of a Hugo starter tips kind of article. If you’re considering Hugo as your new static site generator, I hope you find some useful tidbits in here to help you get it up and running.
</p>
<h3 id="setting-hugo-up" tabindex="-1">Setting Hugo up</h3>
<p>Setting up Hugo isn’t complicated. The docs include two guides: <a href="https://gohugo.io/tutorials/installing-on-mac/"> one for installing Hugo on a Mac</a>, and <a href="https://gohugo.io/tutorials/installing-on-windows/">one for installing it on Windows</a>. From here on forwards I’ll be referring to a Mac setup since a Mac is my main work machine.</p>
<p>I used brew to install Hugo:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">$ brew install hugo</span></code></pre>
<p>I followed the instructions in the installation page and updated brew and ran a few other commands to ensure everything was installed and working as expected. That’s all you need to get Hugo to work and run on your machine. It can’t get any simpler than that. With Jekyll, installation didn’t go as smoothly as I remember spending quite a lot of time to get it set up and running back then.</p>
<hr />
<p>I tend to be a lazy developer sometimes. But that can be good because it pushes me to find the fastest and simplest way to accomplish a task. So the first thing I wanted to do in making the switch to Hugo was a way to automatically migrate all my blog posts without me having to go over each and every one of them to change the front matter in each one. (Seriously, I would have aborted this whole operation if I had to do that. 😅)</p>
<p>Fortunately, as of version 0.15, Hugo provides <a href="https://gohugo.io/commands/hugo_import_jekyll/">a one-liner to migrate from Jekyll</a>. You type the following line into the terminal—replacing <code>jekyll_root_path</code> and <code>target_path</code> with the paths to your current Jekyll directory and the directory you want to set up your new site in, and Hugo will import your current Jekyll site’s files into a new Hugo site directory for you:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">hugo <span class="token keyword">import</span> jekyll jekyll_root_path target_path</span></code></pre>
<p>If you’re not importing a Jekyll site, you may want to check <a href="https://gohugo.io/tutorials/migrate-from-jekyll/">the corresponding docs</a> out, detailing what you need to know about folder structure in Hugo, such as where static assets go, where content and layout templates go, and more.</p>
<p>The next step is to convert your Jekyll templates into Hugo templates, and this is where the bulk of the work is, and where I ended up bumping my head into the walls <em>quite</em> a few times. (But believe me, the end result I have now is <em>very</em> much worth it. Plus, I’ve learned a lot. I’ll be sharing some of what I learned in the next section.)</p>
<p class="note tip">
You may be a different kind of lazy developer. For example, you may prefer to start with a boilerplate that provides you with the setup you need, and that is ready for you to start adding content to right away, especially if you're starting a blog from scratch. In that case, I highly recommend Netlify’s <a href="https://github.com/netlify/victor-hugo">Victor Hugo boilerplate</a>, which comes equipped with everything you need to even get Webpack and Gulp up and running in your site’s Workflow as well. The structure the boilerplate provides is slightly different from what I have below, but not too much.
</p>
<h3 id="diving-into-hugo%3A-technical-details" tabindex="-1">Diving into Hugo: Technical details</h3>
<p>Let me start by saying this: at some point during the migration, I was just tweaking stuff, changing values, names, file names, structure, etc. in the hopes of something magically working and when it does I would go like: “I have no idea how or why this worked.” And as someone else mentioned on Twitter, apparently I’m not the only one who’s had such moments with Hugo. So I’m hoping this (fairly long) post will help some of you thinking about making the switch to Hugo, hopefully saving you some headaches along the way.</p>
<p><strong>Disclaimer:</strong> There’s a lot about Hugo that I <em>still</em> don’t know how to do and find myself Googling sometimes. But I’ve got all the basics and everything I need <em>for now</em> all up and running, and yes, I do know how and why everything I have working now is working the way it is. So, let me share some of that stuff with you. I’ll also share some of the extremely useful articles I found that helped me as well. So think of this article as an idea dump, and a set of reminders for my future self to get back to if I ever get confused again about the basics.</p>
<p class="note">
Please note that not you may end up not using the same process or directory structure I am using. In fact, I am sure that you won’t, unless you have the exact same content types as I do—which is highly unlikely. Also note that yo may find a better way of doing some of the things I am doing now, which is also good. And if you’re already a Hugo pro and find that some things could be done in a better way, please do feel free to share your ways for the rest of us to learn from.
</p>
<h4 id="hugo-folder-structure" tabindex="-1">Hugo Folder Structure</h4>
<p>My site’s local directory structure currently looks like this:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/hugo-folder-structure.png" alt="Hugo Folder Structure" />
<figcaption>
</figcaption>
</figure>
<p>The folders you see in the image above, apart from <code>node_modules</code>, are the ones that Hugo generates for you when you import your site from Jekyll, and these are also the ones you would normally create and set up for a Hugo site. The files at the bottom are the files needed or used by Github and Gulp. The only file that is also used by Hugo is the <code>config.toml</code> file.</p>
<p><code>config.toml</code> contains the site’s configuration variables such as <code>baseURL</code> among many other variables that you may or may not decide to use. It is similar to Jekyll’s yaml configuration file. The Hugo docs provide a long list of available variables and everything you need to know to set up a config file that works for you in <a href="https://gohugo.io/overview/configuration/">this page</a>. My config file contains very few variables for now.</p>
<p><code>/public/</code> is the directory your compiled site will live in. This directory is similar to the the <code>dist</code> directory used in most apps’ directory structure. The rest of the directories are where the dev process happens.</p>
<p>The <code>static</code> directory is where static content such as images, css and js files, audio, video, talk slides, etc. live. It is where I find I spend most of my time working.</p>
<p class="note">After working on the Smashing Magazine redesign I also learned that your structure can be different from the above. The basics are the same, but if you use something like Netlify’s <a href="https://github.com/netlify/victor-hugo">Victor Hugo boilerplate</a>, your setup would be a little different, but the main concepts of what is compiled to where remains almost the same. Also note that the Victor Hugo boilerplate is a fantastic place to start if you want to move to Hugo and use Webpack and Gulp in your workflow. I found Webpack to be overkill for my site given how little JS I have here, but if you do need it, I highly recommend using the boilerplate. I also prefer building from scratch so I can learn the ins and outs of how things work. Whatever works for you, go for it.</p>
<h4 id="creating-and-laying-out-content" tabindex="-1">Creating and laying out content</h4>
<p>For any kind of content you want, be that a static page, a blog post, an index page (for articles, case studies, etc.), you need to create an <code>.md</code> (markdown) file in the <code>/content/</code> directory. This is where <em>all</em> the content is “defined”. After creating the content in its specified directory, you then create (or re-use) a layout template from the <code>/layouts/</code> directory to lay that content out.</p>
<p>Every <code>.md</code> file in the <code>/content/</code> directory corresponds to a page and starts with the page’s front matter, which can be written in either <code>yaml</code> or <code>toml</code> format. Since I wanted to get the feel of a whole new environment, and since most Hugo docs and resources use it too, I decided to use <code>toml</code>. Jekyll uses <code>yaml</code>.</p>
<p class="note">I won’t go into the details of the difference between the syntax used by Hugo versus that used by Jekyll. The Hugo docs as well as, well, Google, do a great job at explaining the differences, so feel free to research further and get back to this article once the differences are clear. I personally did have to spend quite some time learning the new syntaxes (toml, Go templating, etc.) before I felt comfortable using them. But they don’t have a steep learning curve, so don’t let the new syntax intimidate you if you’re not already familiar with it.</p>
<h5 id="defining-content-types-(or-declaring-types-of-content)" tabindex="-1">Defining Content Types (or Declaring types of content)</h5>
<p>Every page’s front matter defines the type of the page/content, which in turn defines what template will be used to lay it out. The type of the page is defined using the <code>type</code> variable. For example, the front matter of an article in the blog section on my site looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">+++</span><br /><span class="highlight-line">type = "blog"</span><br /><span class="highlight-line">description = "..."</span><br /><span class="highlight-line">title = "..."</span><br /><span class="highlight-line">date = ...</span><br /><span class="highlight-line">...</span><br /><span class="highlight-line">+++</span></code></pre>
<p><span class="highlight">The <code>type</code> value an be practically anything</span>, and this is where one of Hugo’s powers truly shines. You can define as many content types as you want. For example, I currently have five types of content on my site: static (pages such as About and Hire), blog (articles like the one you’re reading now), workshops, case studies, and desk (which is a new kind of posts that will be coming soon). I can create as many more content types as I want in the future.</p>
<!-- (This is the first thing I loved about Hugo compared to Jekyll, which does not offer a similar functionality as far as I know. Some devs work around this by using tags to replace types in Jekyll, but in the end, they are all seen as only one type by Jekyll, and so will eventually be less flexible than Hugo’s.) -->
<p class="note update">
A new feature <a href="https://discuss.gohugo.io/t/just-merged-nested-sections-help-take-it-for-a-spin/6909?u=maiki">is being rolled into Hugo now</a> that allows you to create subsections of content, too! This would allow you to, say, create design and development subsections in the articles section, and much, much more. It’s an exciting new addition to the CMS.
</p>
<p>The following screenshot shows what the <code>/content/</code> directory of my site currently looks like:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/content-types.png" alt="" />
<figcaption>The content of the <code>content</code> directory on my Web site.</figcaption>
</figure>
<p>Static pages are created as individual <code>.md</code> files in the root of the <code>/content/</code> directory. Other kinds of content that would need an index page (such as articles, workshops, case studies, etc.) are created inside directories that are named after the content type. For example, workshops are created inside a <code>/content/workshops/</code> directory. My articles live inside the <code>/content/blog/</code> directory. <span class="highlight">Directories like these are also referred to as <em>sections</em>.</span></p>
<p>For every piece of content, you need to define the type of that content. And that can be done in two ways.</p>
<p>The type of the static pages is defined using the <code>type</code> variable, which is in the page’s front matter. The type for the four sections (blog, workshops, case studies and desk), however, is defined using directory structure. When you use directory structure to define a type, you don’t have to define the type in the front matter anymore. For example, a blog post lives inside the <code>blog</code> directory, so its type is automatically set to <code>blog</code>. You don’t need to redefine that type in the front matter of each post.</p>
<p>You can use either the <code>type</code> variable in the front matter or the directory structure way to define content type. Generally, you would use the <code>type</code> variable to define and create static pages; and use the directory structure to define content that requires an index page, such as blog posts.</p>
<p>An important and useful thing to know here is that <span class="highlight">when you define the type of the page using the <code>type</code> variable, the page can be placed anywhere inside <code>/content/</code> directory and the directory structure will be irrelevant</span>. Meaning that you could define a page of type <code>static</code> and place it inside the <code>blog</code> directory, and Hugo will still see it as a static page, and consider its location in the blog folder irrelevant.</p>
<p>But… irrelevant for what? The answer is: to choosing which layout template to use.</p>
<p>You see, each content type will be “mapped” to a certain layout template. You can have different types use the same template, too. I’ll talk more about layout in the next section. But first, let’s create a couple of content pages: two static pages (Homepage and About, for example) and an index page for the blog posts.</p>
<p>But before we do that, I want to point out a note about creating index pages for different sections or types.</p>
<p>The blog section needs to contain an <code>_index.md</code> file inside the <code>/content/blog/</code> directory. This file is the index page for this section (where we will display a list of all the posts). The <code>/content/blog/</code> directory will also host the individual blog posts as well. Check the following screenshot out for a more visual example:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/section-type.png" alt="" />
<figcaption>
Each content type created using directory structure (or, each content <em>section</em>) includes an index page (starts with an underscore), in addition to the individual posts in that section.
</figcaption>
</figure>
<p>Similarly, any and every other content type (or section) will have an index page as well as the individual posts in that section.</p>
<p>So, let’s start creating some pages.</p>
<h6 id="the-homepage" tabindex="-1">The homepage</h6>
<p>The homepage is created by creating an <code>_index.md</code> file inside <code>/content/</code>. You can see it at the top of all static pages in the folder structure of the screenshot above.</p>
<p>The homepage is the only exception to the other pages when it comes to layout in that it will require its own layout in the <code>/layouts/</code> folder (we’ll talk more about layout in an upcoming section) and that layout template has the same name: <code>index.html</code>.</p>
<p>In the front matter of the <code>/content/_index.md</code> you define the type of the page as well as give it a title and description.</p>
<p>The front matter of my homepage looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">+++</span><br /><span class="highlight-line">type = "page"</span><br /><span class="highlight-line">title = "Home"</span><br /><span class="highlight-line">description = "Sara Soueidan — Front-end web developer, author and speaker"</span><br /><span class="highlight-line">+++</span></code></pre>
<p>The description is used in the header partial of the site as a <code><title></code> value like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span> {{ .Page.Description }} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span></code></pre>
<p>The reason I’m not using the <code>title</code> value as the <code><title></code> in the HTML is that, in other pages, the <code>title</code> of the page is used as the name the page will get in the main menu. More on this later.</p>
<p>An <code>.md</code> file in the <code>/content/</code> directory can contain both markdown and HTML. So for the homepage, and since I have no dynamic content such as post listings, I only have the HTML of the page in there. But how does this markdown/HTML content get laid out, and how do we include the header and footer of the page? That all happens in the layout template.</p>
<p>The <code>/layouts/index.html</code> file is the layout used for the homepage. And this is what it looks like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">{{ partial "homepage-header.html" . }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ .Content }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ partial "footer.html" . }}</span></code></pre>
<p><span class="highlight"><code>{{ .Content }}</code> pulls the content from the corresponding page in the <code>/content/</code> folder.</span> So, for the homepage here, it pulls the content of the homepage from the <code>/contents/_index.md</code> file.</p>
<p>Additionally, I’m calling the homepage header as well as the site footer in here using partials.</p>
<p>By default, when you require <code>partial "footer.html" .</code>, <span class="highlight">Hugo will look for the partials—any partial—inside a <code>partials</code> directory which in turn is located inside the <code>layouts</code> directory.</span></p>
<p class="note">
Refer to <a href="http://gohugo.io/templates/partials/">the Hugo docs on partials</a> for details on what the dot at the end of the statemenet refers to and what it does, and how you can customize partial calls.
</p>
<p>And that is how you create the homepage for your site: a <code>/content/_index.md</code> file that contains the homepage content, which is then called and laid out using <code>/layouts/index.html</code>.</p>
<h6 id="adding-a-static-page" tabindex="-1">Adding a static page</h6>
<p>After I got the homepage set up, I wanted to set the rest of the static pages up before moving on to the more dynamic content. So I set out to build the About page.</p>
<p>I had to do a lot of Googling and reading help threads in the Hugo forums and elsewhere to figure this one out. So I hope this post will be most beneficial when it comes to creating static pages—which, surprisingly, turned out to be quite simple.</p>
<p>Static pages are created in the root of the <code>/content/</code> folder, just like the homepage. However, unlike the homepage, the file names will not start with an underscore.</p>
<p>Also unlike the homepage is the fact that you will need to specify the type of the page as well as tell Hugo to include it in the site’s main menu, give it a title and a description.</p>
<p>For the About page of my site, I created a <code>/content/about.md</code> file. The front matter of the page looks like the following:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">+++</span><br /><span class="highlight-line">type = "static"</span><br /><span class="highlight-line">page = "static/single.html"</span><br /><span class="highlight-line">title = "About & Interviews"</span><br /><span class="highlight-line">description = "About Sara Soueidan — Front-end web developer, author and speaker"</span><br /><span class="highlight-line">menu = "main"</span><br /><span class="highlight-line">weight = "1"</span><br /><span class="highlight-line">+++</span></code></pre>
<p>Notice the <code>type</code> value. As I mentioned before, you can specify any type in there. I used <code>static</code> because it literally describes the type of this page. (You’ll also find a lot of online resources using this type for static pages, too.)</p>
<p>The <code>page</code> variable is telling Hugo which template in the <code>/layouts/</code> directory to use.</p>
<p class="note">It is worth noting here that Hugo will automatically use this template even if I don't tell it to. But I remember banging my head against the wall a lot at first when I was trying to figure out how to use the layouts for the pages. I didn't know which layout was going to be used. Despite reading the docs, I still found myself doing and undoing a lot of things and then seeing things work and not work like some sort of magic. Hugo felt like a black box at first that took me a few days to figure out enough to feel comfortable writing about it. When it finally worked, I decided not to touch the front matter anymore because I was afraid I may end up breaking the layouts again. But now that I know better, it's useful to note that you don’t really need the <code>page</code> variable in there.</p>
<p>The <code>title</code> will be used as the title of the link in the menu. (If you look at the menu at the top of this page, you’ll see it as “About & Interviews”).</p>
<p>The <code>description</code> is used in the header partial as mentioned before as a <code><title></code> for the page (which you can see in your browser‘s tab description.)</p>
<p><span class="highlight">The <code>menu</code> variable tells Hugo that this page should have its own link in the main menu.</span></p>
<p><span class="highlight">The <code>weight</code> variable is very useful because it allows you to control the order in which your items appear in the menu.</span> If you don’t use it, Hugo will use its own default order, which is not the order I wanted on my site. You can set weights in negative values as well.</p>
<p class="note">
I will leave the details about using and setting up the main menu for you to research and read about in the Hugo docs for brevity’s sake and because, whether I like to admit it or not, I’m still a little confused about some aspects of the menu; but I got to a point where I had it working the way I wanted it to and then decided not to touch it anymore because I was afraid I would end up breaking it. Again. 😂
</p>
<p>All other static pages are created similarly. The only thing that’s different for each of them is the title and description as well as the order in the menu. The layout used is the same for all of them.</p>
<p>I’d like to note something here that we will get back to later:</p>
<p><strong>Hugo has a specific order in which it chooses which layout to use for every page you create in <code>/content/</code>. We will talk about this more in the layouts section below. So had we not specified a <code>/layouts/static/single.html</code> as a layout template, it would have used a different template from a default folder inside <code>/layouts/</code>. More on this later.</strong></p>
<p>Last but not least, and just like the homepage, the HTML content of the about page is placed in the <code>about.md</code> file, and then pulled into the <code>/layouts/static/single.html</code> template using <code>{{ .Content }}</code>, with a call to the header and footer partials as well. Note how the <code>static</code> type has a corresponding <code>static</code> folder in the <code>/layouts/</code> directory that contains its layout template.</p>
<p class="note">
You don't have to place all the HTML in the markdown file. You can always place the layout HTML such as container elements et al in the layout template, and only have the text content inside the markdown file. I only do it this way because I like it this way.
</p>
<h5 id="content-archetypes" tabindex="-1">Content Archetypes</h5>
<p>You may have noticed in the screenshot above that I also have a folder called <code>/archetypes/</code> at the root of my site. This directory is also related to the content types you create. But it has a specific and very useful purpose.</p>
<p>To explain the purpose of this directory, I’m going to first quote <a href="https://gohugo.io/content/archetypes/">the corresponding page in the Hugo docs</a>:</p>
<blockquote>
<p>In Hugo v0.11, we introduced the concept of a content builder. Using the CLI command <code>hugo new [path/to/my/content]</code>, an author could create an empty content file, with the date and title automatically defined in the front matter of the post. While this was a welcome feature, active writers need more flexibility.
When defining a custom content type, you can use an archetype as a way to define the default metadata for a new post of that type.
Archetypes are quite literally archetypal content files with pre-configured front matter. An archetype will populate each new content file of a given type with any default metadata you’ve defined whenever you run the hugo new command.</p>
</blockquote>
<p>In other words, defining an archetype allows you to speed up your content creation process, because it will populate the front matter of your new page with all the variables you want it to.</p>
<p>For example, suppose I want to create a new case study (which would go in <code>/content/case-studies/</code>). Instead of going into the directory and creating a new <code>.md</code> file for the new page, I can type this one-liner into the terminal and Hugo will create the new file for me:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">hugo new case-studies/my-new-case-study.md</span></code></pre>
<p>And the new case study (<code>my-new-case-study.md</code>) will automatically be populated with variables for: client name, client logo (path to the image), client description, project description, project date, … and many more. The values for these variables will be empty by default, ready for me to fill them up with their values.</p>
<p>The following image shows the variables I have defined in front matter of the <code>case-studies</code> archetype:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/archetype.png" alt="" />
<figcaption>
The variables defined for the case studies archetype. Every time I command hugo to create a new case study for me, it will automatically populate the front matter of the new case study with these variables. These variables are then called in the HTML template of the case study page.
<br />
Also note the other archetypes I have defined in the <code>archetypes</code> directory that correspond to the other four type sections I have on my site.
</figcaption>
</figure>
<p>That’s pretty much all there is to know about archetypes. You can read more about them in the Hugo docs page. They’re pretty handy. You don’t absolutely <em>need</em> to define them, but I reckon you’ll want to.</p>
<h5 id="laying-content-out-with-page-layouts-and-creating-an-index-page-for-posts" tabindex="-1">Laying content out with page layouts and creating an index page for posts</h5>
<p>This is the part that got me the most confused at first. How do I know which layout will be used for this section? How do I know how many templates needs to be in each one or whether I need any at all?</p>
<p>I did a lot of fiddling around, Googling, and, mostly, just trial and error until I managed to make the layouts work. Then I started breaking them so I could understand how and why they worked. I can now finally confidently say that I’ve got the hang of them.</p>
<p>Generally speaking, if you’re creating a very simple blog, you will only need two default templates: <code>list.html</code> and <code>single.html</code>.</p>
<p>The <code>list.html</code> would be used for the pages whose role is to display a list of items, such as the blog’s index page where you see the list of all blog posts you have.</p>
<p>The <code>single.html</code>, as you may have already guessed, is used to lay out the single pages such as the individual blog posts.</p>
<p>These two templates would go in a <code>/_defaults/</code> directory inside <code>/layouts/</code>.</p>
<p>So, if you create a blog with a few posts and don’t give Hugo any special instructions about how to lay their content out, it will go looking in <code>/layouts/_defaults/</code> for templates to use.</p>
<p>I have these layouts in place as a fallback. But I <em>override</em> them.</p>
<p>You can override the default templates by providing templates that fall under the same section name or content type as your content.</p>
<p>In other words, you can create a similar directory structure in the <code>/layouts/</code> directory to the one you have in the <code>/content/</code> directory, and Hugo will follow that structure to determine which template to use.</p>
<p>Or, you can create a directory that has the same name as a <code>type</code> you’ve defined in the content directory, such as the <code>static</code> type we defined for the static images. Hugo will then use the template inside <code>/layouts/static/</code> as a template for all the pages that have <code>type = static</code>, instead of using the default templates.</p>
<p>For example, I created a <code>/layouts/static/</code> directory, and inside that directory I created a <code>single.html</code> file, which Hugo will use to override <code>/layouts/_default/single.html</code> to lay out the static pages.</p>
<p>Once again, the <code>/layouts/static/single.html</code> page is just a template containing the following:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">{{ partial "header.html" . }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ .Content }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ partial "footer.html" . }}</span></code></pre>
<p>where the content of the template is pulled from the respective markdown. So, the generated <code>about.html</code> page is in fact the above <code>/layouts/static/single.html</code> page, with the <code>{{ .Content }}</code> pulled from <code>/content/about.md</code>.</p>
<p>Now, to create an index page for a list of posts as well as the posts I want to list, such as the blog page and the articles it lists, or the workshops page and the workshop details pages, we do something very similar.</p>
<p>Just like we created a directory for the content type defined using <code>type</code> that has the same name as the type itself, we create a directory for each of the other content types that were initially defined using directory structure, and we give that directory the same name as the directory name it has in the content folder.</p>
<p>Or: just like we created a folder in <code>/layouts/</code> named after the content <code>type</code>, we create a folder for each of the content sections (<code>blog</code>, <code>workshops</code>, etc.) and name the folder after the section, so we end up with the same directory structure inside <code>/layouts/</code> as the one we have in <code>/content/</code>.</p>
<p>Confused yet? Don’t be. Here is what it looks like for my site:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/layouts.png" alt="" />
<figcaption>
The structure of the content and layouts directories of my site.
</figcaption>
</figure>
<p>Let’s take a look at the blog section again. The <code>/content/blog/</code> directory has a corresponding <code>/layouts/blog/</code> directory.</p>
<p>Inside the <code>/content/blog/</code> directory I have an index page: <code>_index.md</code> and the blog posts.</p>
<p>Inside <code>/layouts/blog/</code> I have a <code>list.html</code> template as well as a <code>single.html</code> page.</p>
<p>Hugo will use the <code>list.html</code> template for the <code>_index.md</code> page and the <code>single.html</code> template for each of the individual blog posts.</p>
<p>Similarly, each of the other sections also gets its own layout directory containing a <code>list.html</code> and <code>single.html</code> template.</p>
<p>As I mentioned before, you don’t really need all these layouts. And you may have noticed that a few layout pages I have are exactly the same except for their names. The reason I’m doing this is for future flexibility. If I ever want to change the layout for one type or section, I’ll just have to modify its corresponding layout template. If your site is simpler than mine and does not have as many content types, you’ll probably not need to do as much as I did here.</p>
<p>The only exception to the layouts directory structure rule is the homepage, whose layout template is placed in the root of the <code>/layouts/</code> template, named <code>index.html</code>.</p>
<p>Please note that it is important that you check out the default order in which Hugo chooses the template for each page. I highly recommend you do so.</p>
<p>To quote the docs:</p>
<blockquote>
<p>Hugo uses a set of rules to figure out which template to use when rendering a specific page.</p>
</blockquote>
<blockquote>
<p>Hugo will use the following prioritized list. If a file isn’t present, then the next one in the list will be used. This enables you to craft specific layouts when you want to without creating more templates than necessary. For most sites, only the <code>_default</code> file at the end of the list will be needed.</p>
</blockquote>
<blockquote>
<p>Users can specify the type and layout in the front-matter. Section is determined based on the content file’s location. If type is provided, it will be used instead of section.</p>
</blockquote>
<p>You can learn more about this order prioritization in <a href="https://gohugo.io/templates/content/">the corresponding page in the docs</a>.</p>
<h4 id="looping-through-section-lists" tabindex="-1">Looping through section lists</h4>
<p>The last point I want to talk about in the technical Hugo section is listing the posts of a section in that section’s index page.</p>
<p>Again, let’s take the blog in <code>/content/blog/</code> as an example.</p>
<p>Markdown files will, of course, not include any templating logic. So, to list all the posts of the blog, we’ll need to do that in the layout template corresponding for that index page, which is located in <code>/layouts/blog/list.html</code>. The loop and all other templating logic is written in Go.</p>
<p>Now, the loop itself can and will probably be different for a lot of you. After Googling around a lot, I managed to end up with the following loop, which shows the latest five posts and then calls the pagination partial after the loop:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>articles-list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Ranges through content/blog/*.md --></span></span><br /><span class="highlight-line"> {{ range (.Paginator 5).Pages }} </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-title<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{.RelPermalink}}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ .Title }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-meta<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span><span class="token punctuation">></span></span>{{ .Date.Format "January 2, 2006" }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span> {{ if .Params.External }} <span class="token entity named-entity" title="—">&mdash;</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-host<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>for {{ .Params.External.Host }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> {{ end }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-summary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ .Summary }} <span class="token comment"><!-- automatically takes the first paragraph in the .md article --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>small</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{.RelPermalink}}<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>read-more-link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more ››<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>small</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> {{ end }}</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ partial "pagination.html" . }}</span></code></pre>
<p class="note">
Don’t judge the HTML in that loop yet. I haven’t worked on my site in a long time. It needs and could use some improvement. The markup will change soon.
</p>
<p>The <code>{{ range .Paginator.Pages }}</code> part is the key here. <span class="highlight">Each <code>.Paginator</code> you use in any section’s index page will loop through and display the posts <em>in that section</em>.</span> The <code>(.Paginator 5).Pages</code> tells Hugo to only list five posts. The loop in the code above will go over the posts in the <code>blog</code> section, listing only the most recent five. A similar loop in the <code>layouts/workshops/index.html</code> file would loop over the workshops inside <code>/content/workshops/</code> and display a list of the posts in there.</p>
<p class="note">
I'm still confused by some of the global site and page variables in Hugo. I'm currently good with what I have and am using, but if I ever need more flexibility, options, functionality, I will need to dig more into the docs and get a lot more out of Hugo’s logic than just a simple loop. You should too.
</p>
<p>As for the <code>pagination.html</code> partial, mine currently looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">{{ $baseurl := .Site.BaseURL }}</span><br /><span class="highlight-line">{{ $pag := .Paginator }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ if gt $pag.TotalPages 1 }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>center pagination<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ range $pag.Pagers }}{{ if eq . $pag }}<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button button--disabled<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ .PageNumber }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>{{ else }}<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>{{ $baseurl }}{{ .URL }}<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>{{ .PageNumber }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>{{ end }}{{ end }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clearfix<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ if .Paginator.HasPrev }}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button pagination__button--previous<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Previous Page<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ .Paginator.Prev.URL }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Newer Articles</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ else }}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button pagination__button--previous button--disabled<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Newer Articles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ end }}</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> {{ if .Paginator.HasNext }}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button pagination__button--next<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Next Page<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ .Paginator.Next.URL }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Older Articles</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ else }}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination__button pagination__button--next button--disabled<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Older Articles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ end }}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>../article-archives/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button button--full<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>See a list of all articles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ end }}</span></code></pre>
<p>Feel free to dig into the variables more and learn more about them. I find that the code above is understandable as it is, but, again, if you need more functionality, the docs and forums would probably be able to help more.</p>
<h4 id="creating-an-archive-page" tabindex="-1">Creating an Archive page</h4>
<p>In addition to the default blog page, I wanted to add an archive page that lists all of my articles in one pagination-less page. This was not as straightforward as I’d hoped it would be. The docs didn‘t help me much and I again found myself Googling. I came across <a href="https://parsiya.net/blog/2016-02-14-archive-page-in-hugo/">this extremely useful article</a>, and pretty much used the same technique the author is using.</p>
<p>For the archive page, I created a static page inside <code>/content/</code> and gave it a new <code>type</code>: <code>archive</code>. The page will use the layout inside <code>/layouts/archive/single.html</code>.</p>
<p>Inside the layout template, I loop through the articles using a loop similar to the blog’s loop, but with an important modification:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- /layouts.archive/single.html --></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">{{ range where .Site.Pages "Type" "blog" }} </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-title<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{.RelPermalink}}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ .Title }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-meta<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span><span class="token punctuation">></span></span>{{ .Date.Format "January 2, 2006" }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span> {{ if .Params.External }} <span class="token entity named-entity" title="—">&mdash;</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-host<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>for {{ .Params.External.Host }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> {{ end }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post-summary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> {{ .Summary }} <span class="token comment"><!-- automatically takes the first paragraph in the .md article --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>small</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{.RelPermalink}}<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>read-more-link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more ››<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>small</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">{{ end }}</span></code></pre>
<p><strong>Heads up:</strong> <span class="highlight"><code>.Site.Pages</code> will loop through <em>all</em> pages you have on your site. In other words, it will list every single <code>.md</code> file you have inside <code>/content/</code>.</span> In order to tell Hugo to only display posts inside the <code>/content/blog/</code> section, you “filter” the pages using <code>"Type" "blog"</code>. Similarly, if you want to create an archive page for a different section, use that section’s type name as a filter.</p>
<p>And that’s it.</p>
<h3 id="hosting-on-netlify" tabindex="-1">Hosting on Netlify</h3>
<p>Github pages was my choice for hosting this Web site over the last couple of years. At some point, it started coming short. There also seemed to be some weird caching issue happening all the time where I found myself having to push changes to the repository <em>twice</em> in order for the latest change to show up (I guess the cache may not have been invalidated whenever it needed to). So I’d start creating “dummy commits” only to clear the cache and be able to see the changes I’d made live.</p>
<p>Now, I’m not sure if this was indeed a cache problem, although that’s exactly what it seemed like it was. I also don’t know if anyone can replicate this issue. No, I haven’t asked Github support about it. I’ve hated my Web site so much that I kinda thought “I’ve got a lot worse happening locally to even worry about this online issue”, so I just ignored it all along.</p>
<p>I also saw how blazing fast <a href="https://www.netlify.com/">Netlify</a> was when I worked on Smashing Magazine. Netlify also offers to “make your site or web-app many times faster by bringing it closer to your users. Instead of a single server, push to a global network of intelligent CDN nodes that also handle asset fingerprinting, automatic caching headers, and smart redirect & rewrite rules.”</p>
<p>And to top that off, if you’re a developer and/or you’re doing open-source work, Netlify offers you a free Pro subscription for life. All they ask for in return is a mention of Netlify on your site or application. For me, this was no issue at all as I always mention where my site is hosted in the footer. So, I signed up for the free Pro subscription. Free, fast hosting! Woohoo!</p>
<p>It only takes a few clicks to get your site up.</p>
<ul>
<li>Create an account on <a href="http://netlify.com/">netlify.com</a></li>
<li>Link your Netlify account to your code repository. Mine is hosted on Github so I just connected it (you do all this from the Netlify dashboard).</li>
<li>Specify the build destination folder as well as a build command. <code>hugo</code> is the build command I used, and <code>public</code> is the build directory. (See screenshot below.)</li>
<li>Set up a custom domain. This also includes making some DNS changes.</li>
<li>It took literally only 3 clicks to get an SSL certificate and HTTPS running for the site.</li>
<li>And… well… you’re done.</li>
</ul>
<p>I should probably mention that I did face a couple of annoyances while I was making the switch but it was not Netlify’s fault. And the Netlify team was super helpful and swiftly debugging the issues I was having. After making some DNS changes in my domain registrar’s dashbard, it took a few hours for my site to be online on my custom domain.</p>
<p>A couple of tips worth mentioning:</p>
<ul>
<li>Add your local <code>/public/</code> folder to your <code>.gitignore</code> file. Netlify will build your site for you on their servers. In order to avoid any possible conflicts, don’t push your public directory to the repo. I keep mine local now. I had an issue with some templates rendering when I was committing it before.</li>
<li>Check the Hugo version you’re using (<code>hugo version</code>) against the version that Netlify uses. I had some build errors preventing the deploy at first which were a result of my version being ahead of that of Netlify’s. Let the team know if you face any similar issues so they can add a build environment variable to your site to match your local version.</li>
</ul>
<p>Here’s what a part of my Netlify dashboard looks like:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/netlify-dashboard.png" alt="" />
<figcaption>
Deploy settings, build environment variables as seen on my Netlify dashboard.
</figcaption>
</figure>
<p>I also love that Netlify provides options to optimize and bundle assets for you, improving your site’s overall performance sometimes as well.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/article-assets/hugo-netlify/netlify-dashboard-2.png" alt="" />
<figcaption>
Asset optimization options as seen on my Netlify dashboard.
</figcaption>
</figure>
<p>I saw quite a few performance improvements and more green A’s in the <a href="https://webpagetest.org/">webpagetest.org</a> results that used to be red before. I still have a few more improvements to make.</p>
<h3 id="summary-of-current-set-up" tabindex="-1">Summary of current set up</h3>
<ul>
<li>This Web site’s source code is hosted on Github.</li>
<li>I use Hugo as a static site generator.</li>
<li>Pushing to repo automatically deploys using Netlify.</li>
<li>Hosting with Netlify for free with the developer’s Pro plan.</li>
</ul>
<p>It’s worth mentioning at this point that compiling my entire site now after every change, without having to filter out old articles or anything like that, takes Hugo no more than 40 seconds every time. To be more accurate, <span class="highlight">it takes Hugo around 39ms to compile my entire site now, compared to the <em>minutes</em> needed by Jekyll before</span>, even after using flags like <code>--incremental</code>.</p>
<h3 id="future-plans" tabindex="-1">Future Plans</h3>
<p>These include some but not all of the things that have been on my to-do list for the last couple of years that I have been postponing, partially because of the previous Jekyll situation:</p>
<ul>
<li><strong>Starting a mailing list.</strong> This will be coming later this month.</li>
<li>A new section on the site for articles that don’t fit into the technical articles section.</li>
<li>Improving the site code enough to not be embarrassed by it anymore and make the repo public on Github.</li>
<li><strong>Make the site available offline.</strong> And make it even <em>faster</em>.</li>
<li>There will be <strong>an AMA</strong> but not the traditional Github-hosted AMA. There are aspects of the Github-based format that I don’t like. More info and details will also be out as soon as the newsletter is.</li>
<li><strong>Write more frequently.</strong> I’m letting way too many ideas slip that I should honestly be turning into blog posts. I promised myself to write more even if the article ideas are not as deep-dive as my usual articles are. And this post is a start.</li>
</ul>
<h3 id="final-words%3F" tabindex="-1">Final Words?</h3>
<p>I’ll let Agnes express how I feel about my current setup, even though I know I can and will be improving some details more in the future:</p>
<div style="width:100%;height:0;padding-bottom:55%;position:relative;"><iframe src="https://giphy.com/embed/uHSbNh58qwIwM" width="100%" height="100%" style="position:absolute" frameBorder="0" class="giphy-embed" allowFullScreen=""><p>An animated image of Agnes from Despicable Me grabbing her friend and most excitedly saying “I’m so happy”.</p></iframe></div><p><a href="https://giphy.com/gifs/despicable-me-excited-friday-uHSbNh58qwIwM">via GIPHY</a></p>
<p>At least for now I know I have a setup that won’t give me any headaches whenever I want to make any new changes to my Web site. I’m also enjoying writing articles for the blog again, which means that you can expect more to come in the next few weeks.</p>
<p>Thank you for reading.</p>
Building a fully-accessible help tooltip
2017-01-24T00:00:00Z
https://sarasoueidan.com/blog/accessible-tooltips/
<p class="deck">
Today is one of those days that started out with a Google search for yet another accessibility question/concern. I’m working on a new project for my client <a href="https://sarasoueidan.com/case-studies/provata/">Provata</a> and part of that project is to build a sweet and seemingly simple help tooltip which explains to the reader/user what the <a href="https://www.cvdriskchecksecure.com/framinghamriskscore.aspx">Framingham calculator</a> is.
</p>
<p>The tooltip is triggered by a small help icon like the one shown in the top right corner of this screenshot:</p>
<img src="https://sarasoueidan.com/assets/images/provata-tooltip.png" alt="The tooltip trigger as provided in the project mockup" />
<p>As with every project, starting out by thinking about what HTML element to use to mark up a component was the first thing I did. But it turned out there is no HTML element to mark up a tooltip like this. And when we have no semantic elements to mark up our components, we are faced with the challenge to make sure that assistive technologies (ATs)—which are usually capable of understanding and conveying meaning via semantics—are able to understand what our elements really are and what they do.</p>
<h3 id="aria-attributes-to-the-rescue%3F" tabindex="-1">ARIA attributes to the rescue?</h3>
<p>Making elements more readable by ATs when HTML isn’t enough is possible using ARIA attributes. For example, you can get by creating a progress bar without actually using the not-too-well-supported HTML <code><progess></code> element. Making that bar look and behave like a bar is straightforward using some CSS, but if you’re using <code>div</code>s and <code>span</code>s to build it, you need to give ATs something more to make something of those non-semantic elements. This is where ARIA attributes like <code>role="progressbar"</code> and its companions, the helpful <code>aria-valuenow</code>, <code>aria-valuemin</code> and <code>aria-valuemax</code> (among others), come in handy. Sprinkling in some simple JavaScript, you upate the value inside <code>aria-valuenow</code> as the user progresses through whatever steps you provided them with and you’re generally good to go. You can read more about this <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_progressbar_role">on MDN</a> if you’re curious or unfamilir with this.</p>
<p>Great.</p>
<p>We have an ARIA attribute value to help us indicate that a certain element is a tooltip: <code>tooltip</code>. Using <code>role="tooltip"</code> ATs know that this element is indeed a tooltip.</p>
<p>But tooltips are usually triggered by an action performed on another element.</p>
<p>Let’s first get back to the basics. According to Wikipedia:</p>
<blockquote>
<p>The tooltip or infotip or a hint is a common graphical user interface element. It is used in conjunction with a cursor, usually a pointer. The user hovers the pointer over an item, without clicking it, and a tooltip may appear—a small “hover box” with information about the item being hovered over. Tooltips do not usually appear on mobile operating systems, because there is no cursor (though tooltips may be displayed when using a mouse).</p>
</blockquote>
<p>I have a problem with the “Tooltips do not usually appear on mobile operating systems, because there is no cursor” part because mobile users have just as much right to get information on a page as non-mobile users. Whether you’re using a mouse or your finger, you should be able to understand what the Framingham score is, so this tooltip should be accessible in all contexts. And this is exactly where the fun starts.</p>
<h3 id="triggering-the-opening%2Fclosing-of-the-tooltip" tabindex="-1">Triggering the opening/closing of the tooltip</h3>
<p>I knew I couldn’t rely on hover alone (even though my client only requested hover) because, unless you have a touch screen with a fancy digital pen which allows for hover to be triggered, you won’t be able to see the hint inside the tooltip, and that is unacceptable.</p>
<p>I set out to make the tooltip show when the help icon is both hovered and clicked.</p>
<p>It is worth noting at this point that sometimes tooltips work on both touch and pointer screens without having to do any extra work, such as those that are shown as help labels on <code>input</code> fields, like <a href="http://heydonworks.com/practical_aria_examples/#input-tooltip">this example</a> by Heydon Pickering. In scenarios like this one, <code>role="tooltip"</code> in combination with <code>aria-describedby</code> are enough to indicate to ATs that this piece of text is indeed a tooltip which describes the content or functionality of the input field. Marking the tooltip up in this case is very easy because it’s already clear enough what it is. All that remains is for you to show/hide the text when the <code>input</code> field is focused, and this can be done using a couple of lines of CSS alone. The experience is great on mouse and touch screens, and everyone gets to experience it the same way.</p>
<p>However, things aren’t so straightforward when you have an example like the one in my project. And apparently I’m not the only one who’s been here and has been just as confused as to how to approach this.</p>
<p>Here are a few of the ideas that crossed my mind as I was thinking about the implementation of this:</p>
<ul>
<li>Use a <code><button></code> to trigger the opening and closing of the tooltip. Tooltip would initially be hidden so that ATs won’t read it out loud as the user moves down the page. The tooltip interrupts the flow of the content in our project’s case and so should only be shown on demand.</li>
<li>“on demand” means when the user hovers/clicks/taps/focuses the trigger.</li>
<li>The tooltip would initially be hidden using <code>display: none</code> and only shown on demand.</li>
<li>Since semantics and “how machines would read this” is how I start all of my HTML code, I thought:
<ul>
<li>if I use <code><button></code> I’ll have to use <code>aria-controls</code> to indicate what that button controls and that it will show/hide some element when it is clicked. All good.</li>
</ul>
</li>
<li>Speaking of semantics, I could also use a link (<code><a></code>) that links to a specific section on the page, which in my case is the element containing the hint.</li>
<li>When using a link, the user will <em>jump</em> to the section on the page to which they’re being taken. I hate page jumps. I avoid them like the plague unless the decision to jump is intentional. (For example, a “Back to top” link on a very long page.)</li>
<li>In order to avoid the jump when using <code><a></code>, <em>and</em> in order to make the <code><button></code> open <em>and</em> close the tooltip, I need JavaScript.</li>
<li>For some holistic reason, I wanna avoid JavaScript and use it only when necessary. What if the user is a senior using some very old machine that has JS disabled?</li>
</ul>
<p>Usually, when I’m torn between two solutions and when the JS-no-JS thoughts start floating in my mind, I look for second opinions. My first source of a second opinion is Google. I thought: “someone else must have built one of these before, and so must have somebody else too, so let’s see how they approached it and solved these issues”.</p>
<p>At this point I was still on the “I want to do this without JS if possible” boat, but believe it or not I love JS and was completely open to using it if the JS-based solution is better than all of the others.</p>
<p>Googling got me to land on a question that was exactly the same as mine. I found a lot of similar questions but all of them were more like the tooltip-on-input example. I found <a href="http://webaim.org/discussion/mail_thread?thread=5041">a very old thread</a> started by <a href="http://twitter.com/zomigi">Zoe Gillenwater</a> who had the same question back in 2011 that I had today. I am aware that some technical details in there would/could be invalid today but the general principles are still valid. I highly recommend you read through that thread before continuing to read this article because everything else in here is based on some of the insights I got from that thread. Main points mentioned in the thread included:</p>
<ul>
<li>Avoid using <code><button></code> and stick to <code><a></code> because links can be used to practically anything whereas buttons should be left for using in forms. I have to say <strong>I disagree here</strong>. If you know a very good reason to avoid using <code><button></code>, please <a href="https://twitter.com/SaraSoueidan">let me know</a>.</li>
<li>Using <code><a></code> you have to consider/remember:
<ul>
<li>If you use <code><a></code> and have the hint be included inside of it:
<ul>
<li>The code looks like this:<pre><code> <a href="#" aria-describedby="#tip">
<!-- your icon here, img or svg -->
<span id="tip"> Your hint text here </span>
</a>
</code></pre>
</li>
<li>The text will be readable with the flow (which is <em>not</em> favorable in my case) because the content of the hint interrupts the flow of the information and data displayed in the main content.</li>
<li>Using CSS only, you can hide and show the text with <code>display: none</code> and <code>display: block</code> respectively when the link is hovered. This is good for sighted users using a mouse. However,…</li>
<li>If you hide the text using <code>display: none</code> inside the <code><a></code> and show it on focus, ATs won’t be able to read the shown text because the contents of the link are announced on initial <code>focus</code> only and won’t be re-announced when the text is displayed inside the link with <code>display: block</code>.</li>
<li>Testing this out I noticed that since the link doesn’t really link to anywhere, the user will jump to the top of the page when they tap the link on a touch screen. This is particularly horrible in our case because we have a long page and jumping up will only confuse the users. <strong>This can be solved using JavaScript to <code>preventDefault</code> on click.</strong></li>
<li><em>(My opinion: I really dislike the fact that the link doesn’t really link anywhere. It’s a self-containing link, if that makes any sense, which negates the role of the link to begin with, in my opinion. So even though I tested it out of curiosity, I knew I didn’t want to use this.)</em></li>
</ul>
</li>
<li>If you use <code><a></code> and have it link to a separate piece of text (not a descendant of the link itself):
<ul>
<li>The code would be along the lines of: <code><a href="#tip"><!-- icon here --></a><div id="tip"> <!-- tooltip text here --> </div></code></li>
<li>The link still causes a page jump unless using JavaScript.</li>
<li>On touch screens, tapping the link focuses it which in turn opens the tooltip, but the link image remains the same and there is no way to close the tooltip unless the user clicks anywhere outside it to remove its focus, which is not intuitive because there’s a lot of cognitive effort and guesswork and “how do I close this” guesswork and frustration involved, especially since the tooltip covers the content when open.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Every single no-JS solution came with a very bad downside that negatively affected the user experience. JavaScript is the way to make this work properly for every user in every context. It solves every one of those problems mentioned, except for the “this link doesn’t link anywhere” problem which can only be solved by, well, not using a link that links nowhere. :D (Some may disagree that a link not linking anywhere is not particularly wrong or invalid, but it’s not something I’d personally do.) I ended up choosing a solution that works, using JavaScript, and has a not-too-bad fallback as well. This pretty much covers the main thoughts and considerations I had throughout this.</p>
<h3 id="conclusion(s)%3F" tabindex="-1">Conclusion(s)?</h3>
<p><strong>JavaScript is imperative to make fully-accessible interactive components.</strong> Sure, you can get away without using it in some cases, but a lot of accessibility requires JavaScript. Some ARIA roles and attributes are absolutely necessary to make components accessible, and many of those will simply not behave as they need to unless you make them work with JavaScript. Since my client is a health company, it is more likely that their users have one form of disability or the other than it is that they have JS disabled, so it’s safe to worry about not implementing JavaScript than about it not running.</p>
<p>If you’re interested in learning more about which attributes require what, I highly recommend checking out the <a href="http://whatsock.com/training/matrices/">ARIA Role Matrices</a> from WhatSock. It provides and easy-to-scan and read overview.</p>
<p>Paul J Adam has also created <a href="http://pauljadam.com/demos/aria-role-tooltip.html">a demo</a> to show the different ways to show tooltips on hover. His examples still use JavaScript to make the components more accessible by toggling ARIA attributes as the tooltips open/close. This one is also definitely worth checking out.</p>
<p><em>Did I miss something? <a href="https://twitter.com/SaraSoueidan">Let me know on in a tweet</a>.</em></p>
Mimic Relative Positioning Inside an SVG with Nested SVGs
2016-06-27T00:00:00Z
https://sarasoueidan.com/blog/mimic-relative-positioning-in-svg/
<p class="deck">Positioning elements inside an SVG image is very similar—if not identical—to positioning elements absolutely in HTML. Every element in SVG is positioned "absolutely" relative to the SVG viewport, and the position inside the viewport is governed by the <i>current coordinate system in use</i>. But this similarity in positioning elements should not conceal the fact that there is a fundamental difference between SVG elements and HTML elements: <strong>SVG elements do not have a box model like HTML elements do in CSS</strong>.</p>
<p>Before we move on, let’s quickly review what a box model is in CSS and how it affects positioning things.</p>
<h3 class="deeplink" id="box-model">Quick Review of The Box Model in CSS</h3>
<p>Every HTML element has a box model in CSS that is composed of four boxes: the <em>content box</em>, the <em>padding box</em>, the <em>border box</em>, and the <em>margin box</em>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/box-model.png" alt="A visual representation of the CSS Box Model" />
<figcaption>
The box model of an element in CSS—includes the content, padding, border, and margin areas. Image borrowed from the <a href="http://tympanus.net/codrops/css_reference/box-sizing/"><code>box-sizing</code> entry</a> in the <a href="http://tympanus.net/codrops/css_reference/">Codrops CSS Reference</a>.
</figcaption>
</figure>
<p>Normally, when an element’s size is set, the width and height properties determine the width and height of the element’s content box. Any padding added to the element will increase the total computed width and/or height of the element—this is how the default box model works in regards to sizing the element. The <code>box-sizing</code> property allows you to control how the sizing of an element’s dimensions works. More specifically, using the box-sizing property, you can tell the browser to include the padding width and/or border width in the width of the element, without increasing that width. This is useful for many use cases, but mostly so for when you’re building grid systems in CSS, for example. You can learn all about this property and its values in <a href="http://tympanus.net/codrops/css_reference/box-sizing/">this entry</a> over on Codrops.</p>
<p>An element’s box model is also <strong>used to create a positioning context for the contents of the element</strong>, where applicable, or for the element itself.</p>
<p>When the value of an element’s <code>position</code> changes from the default <code>static</code> value, it either creates a positioning context for its descendants or for itself. Whenever the default position changes, a positioning context is needed to specify where and how an element is going to be positioned outside the default page’s content flow. (You can learn more about this subject <a href="http://tympanus.net/codrops/css_reference/position/">here</a>.)</p>
<p>If you want to remove an element from the page’s content flow, you can do that by positioning it absolutely. Positioning an element absolutely means it will be positioned relative to one of its ascendants, using that ascendant’s box as a positioning context.</p>
<p>Each positioning context, however, requires a coordinate system. The cooridnate system is established by the dimensions (width and height) of the element’s box model. Any descendant of the element will then be positioned inside and relative to the element using this coordinate system.</p>
<p>In SVG, however, there is only one coordinate system by default used to position elements inside the viewport: the <i>current coordinate system in use</i>, established by the SVG <code>viewBox</code>. And so when an element needs to be positioned inside an SVG, it is positioned relative to the entire SVG viewport. </p>
<p class="note">Technically, there are <strong>two</strong> default coordinate systems in an SVG. But only one of those is relevant when dealing with positioning SVGs unless you explicitly change the values of both. If you're not familiar with SVG coordinate systems and how they're established and used, I highly recommend reading <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">this article</a> before continuing through this one. In this article, we'll be dealing with the ‘normal’ case where we only need to deal with one.</p>
<p>Individual elements don’t have a box model and therefore don’t have their own coordinate systems that can be used as positioning contexts for other elements. So, what if you do want to position an SVG element relative to another SVG element or group of elements?</p>
<p>The answer is: nested <code><svg></code>s.</p>
<h3 class="deeplink" id="nesting-svgs">Nesting SVGs</h3>
<p>One of my favourite things about SVG is that it’s an image defined by markup. And that markup is what gives us a lot of power over the contents of that image and how they are displayed.</p>
<p>You can nest <code><svg></code>s. That is, you can put an <code><svg></code> inside another <code><svg></code>. And then you can put another <code><svg></code> inside that <code><svg></code>. And then you can put yet another <code>svg</code> inside that <code>svg</code>. And you can go on and on.</p>
<p>You can nest SVGs as deeply as you want. How many levels deep you want to go depends on what you want to do and whether or not you need to, of course. I’ve personally never needed to nest SVGs more than two levels deep.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- some SVG content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- some inner SVG content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- other inner SVG content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<h4>Some notes about nested <code><svg>s</svg></code></h4>
<ul>
<li>The inner <code><svg></code> element does not require specifying a namespace (<code>xmlns</code>) on it because it is assumed to be the same namespace as the outer <code><svg></code>’s namespace. Even the outer (root) <code><svg></code> does not require a namespace if it is embedded inline in an HTML5 document.</li>
<li>You can use a nested SVG to group elements together and then position them inside the parent SVG. Of course, you can group elements inside an SVG using the group tag <code><g></code>, but using an <code><svg></code> instead has a few advantages, such as being able to specify the group’s <code>width</code> and <code>height</code>, and positioning it using absolute values <code>x</code> and <code>y</code> instead of having to use transforms (for <code><g></code>). By specifying a width and height to the <code><svg></code>, you restrict the content inside it to the bounds of the viewport that is defined by the <code>width</code>, and <code>height</code> attributes. Any content that lies beyond these bounds will be clipped.</li>
<li>Percentage values specified for elements inside an inner <code><svg></code> will be calculated relative to that <code>svg</code>, not relative to the root <code>svg</code>. Percentage values specified on the inner <code><svg></code> itself will be calculated relative to the root <code>svg</code>.</li>
</ul>
<h3>So, Why Nest <code><svg>s</svg></code>?</h3>
<p>One use case for nesting SVGs is creating interesting responsive effects where the contents of the SVG would hide or reveal other portions of content at different viewport sizes.</p>
<p>Such an example is the following SVG illustration of a small bird inside an egg:</p>
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1.png" style="max-width: 400px; display: block; margin: 0 auto;" alt="The SVG image we will be using contains a bird covered by an egg made of two shells." />
<p>Normally, if the <a href="http://tympanus.net/codrops/2014/08/19/making-svgs-responsive-with-css/">SVG is responsive</a>, resizing the screen would make the entire SVG smaller while maintaining the positions of the content inside of it and the spatial relationships between them:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-resized.png" alt="The SVG as it looks when the viewport shrinks. It looks the same, but smaller." />
<figcaption>Resizing the responsive SVG in the browser makes the SVG shrink in size, without affecting the position and spatial relationships of the content inside of it.</figcaption>
</figure>
<p>By nesting <code>svg</code> elements, we can create separate “layers” inside the root <code><svg></code> that we can then control so that the contents of these layers would change position inside the root <code>svg</code> as the viewport size changes. By doing that, we can show and hide different portions of content inside the SVG as desired.</p>
<blockquote class="pull-quote">
By nesting <code>svg</code> elements, we can create separate “layers” inside the main <code><svg></svg></code>
</blockquote>
<p>For example, we can separate the above illustration into 3 layers that would reveal the small bird on smaller sizes:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-2.png" alt="The bird illustration with the two egg shells changing position on smaller sizes, revealing the bird underneath." />
</figure>
<p>This effect is achieved by using different <code>preserveAspectRatio</code> values on each of the inner <code>svg</code>s. This ensures that the contents of each <code>svg</code>—i.e. the contents of each ‘layer’, ‘sticks’ to either edge of the root SVG, thus revealing the content in between.</p>
<p>I’ve written a detailed article about how to achieve this; so, if you’re interested, do <a href="https://sarasoueidan.com/blog/nesting-svgs/">check it out</a>.</p>
<h3 class="deeplink" id="relative-position-in-svg">‘Relative’ Positioning in SVG Using a Nested <code>svg</code></h3>
<p>The fact that contents of an inner <code>svg</code> are positioned relative to that <code>svg</code> itself gets us one step closer to positioning elements relative to other elements as opposed to being relative to the root <code>svg</code>.</p>
<p>But how exactly does a nested <code>svg</code> enable us to position one element <em>relative to another non-<code>svg</code> element</em>?</p>
<!-- The answer is: by combining it with an element’s __bounding box__. -->
<p>Before we answer that question, we need to understand what an SVG element’s <strong>Bounding Box</strong> is.</p>
<h4 class="deeplink" id="svg-bounding-box">What is a Bounding Box?</h4>
<p>Not all SVG elements are created equal. The powerful thing about SVG is that its basic shapes allow us to create all kinds of non-rectangular shapes: from arbitrary paths, to open or closed polylines and polygons, to circles and ellipses.</p>
<p>Because of the nature of these elements and their lack of a CSS box model, the SVG specification compensates for the lack of a box model by <a href="https://www.w3.org/TR/SVGTiny12/coords.html#BoundingBox">introducing the concept of a <strong>bounding box</strong></a>:</p>
<blockquote>
The bounding box (or "bbox") of an element is the tightest fitting rectangle aligned with the axes of that element's user coordinate system that entirely encloses it and its descendants.
</blockquote>
<p>In simpler words, a bounding box is the smallest rectangle that you can draw around an element, that encloses the entire element—all its points and edges.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/bounding-box-ai.png" alt="The bounding box of a spiral object drawn in Adobe Illustrator." />
<figcaption>
The light blue rectangle represents the smallest rectangle around the spiral shape, and thus is a visual representation of the shape’s bounding box. By selecting an element in the graphics editor, you can retrieve the properties of the element’s bounding box. In the above screenshot, these properties are available in Adobe Illstrator’s <i>Transform</i> panel.
</figcaption>
</figure>
<p>Three kinds of bounding boxes can be computed for an element:</p>
<ol>
<li>
<p><strong>The object bounding box</strong> is the bounding box that contains only an element’s geometric shape.</p>
</li>
<li>
<p><strong>The stroke bounding box</strong> is the bounding box that contains an element’s geometric shape and its stroke shape.</p>
</li>
<li>
<p><strong>The decorated bounding box</strong> is the bounding box that contains an element’s geometric shape, its stroke shape and its markers.</p>
</li>
</ol>
<p>An element’s bounding box is characterized by properties that can be retrieved using the <code>getBBox()</code> method—the SVG equivalent of <code>getBoundingClientRect()</code>: <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code>.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">var</span> svgElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'el'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">bbox <span class="token operator">=</span> svgElement<span class="token punctuation">.</span><span class="token function">getBBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> bbox<span class="token punctuation">.</span>x <span class="token punctuation">)</span> <span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> bbox<span class="token punctuation">.</span>y <span class="token punctuation">)</span> <span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> bbox<span class="token punctuation">.</span>width <span class="token punctuation">)</span> <span class="token punctuation">;</span> </span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> bbox<span class="token punctuation">.</span>height <span class="token punctuation">)</span> <span class="token punctuation">;</span></span></code></pre>
<p><strong>Using the element’s bounding box, we can fake the presence of a coordinate system around that element, which we can then use to position other elements.</strong></p>
<blockquote class="pull-quote">
Using the element’s bounding box, we can fake the creation of a coordinate system around that element, which we can then use to position other elements.
</blockquote>
<p>More specifically, we will be creating and using an inner <code><svg></code> to establish a new cooridnate system around an element. The properties of the <code><svg></code> will be defined by the properties of the element’s bounding box: the x, y, width, and height properties.</p>
<h4 class="deeplink" id="element-coordinate-system">Creating a new coordinate system around an SVG element</h4>
<p>Suppose we have the following SVG image (<a href="http://www.vecteezy.com/vector-art/82694-birds-in-nest-vector">courtesy of Vecteezy</a>) with the bird and the nest:</p>
<img src="https://sarasoueidan.com/assets/images/bird-nest.svg" style="border: 2px solid #eee;" alt="An SVG containing a bird and a nest, with the bird positioned far from the nest." />
<p>Let’s have some fun. The bird in the above image is trying to get back to its nest. (My idea of fun is, admittedly, not than fun.)</p>
<p>Normally, we are able to position the bird above the nest by specifying its position inside the SVG using the entire SVG canvas’s coordinate system.</p>
<p>We can certainly do that.</p>
<p>But, ideally, we’d be able to position it by using percentage values that would be calculated relative to the nest’s “box”. We can mimic that by creating a coordinate system around the nest using our new <code><svg></code> element. The <code><svg></code> element has its own coordinate system established by its width and height. We will use <em>that</em> coordinate system to make up for the missing coordinate system on the nest.</p>
<p>Then, we move the bird (the actual bird content) into that <code><svg></code> tag. By being contained by the <code><svg></code>, the bird‘s position will be calculated relative to the coordinate system established on that <code><svg></code>.</p>
<p>But to create the relative connection between the bird and the nest elements, we need the positioning context of the bird—which is our <code><svg></code>—to resemble a coordinate system around the nest.</p>
<p>In order to do that, we will position the <code><svg></code> on top of the nest, <em>visually</em>. It’s important to note here that the inner SVG does not actually wrap the nest—the nest elements are not contained inside the <code><svg></code> tag. We’re only positioning the <code><svg></code> <em>on top</em> of the nest, visually, so that it seems as though <strong>the <code><svg></code> is the <em>visual representation</em> of the nest’s coordinate system</strong>.</p>
<p>In order to determine the exat position of the <code><svg></code> (its <code>x</code> and <code>y</code> position inside the root <code>svg</code>) and its dimensions, we will be using the nest’s bounding box properties.</p>
<p>The position of the <code><svg></code>—the <code>x</code> and <code>y</code> values—will be equal to the <code>x</code> and <code>y</code> values of the nest’s bounding box. That is, the bounding box of the group of elements forming the nest. (Groups can have bounding boxes, just like single elements can.) The inner <code>svg</code> will also have explicit height and width values which are equal to the height and width of the nest’s bounding box.</p>
<p>Here is what it looks like visually:</p>
<img src="https://sarasoueidan.com/assets/images/nest-bbox.png" style="border: 2px solid #eee;" alt="A visual respresentation of the nested svg element positioned on top of (or around) the nest looks like the bounding box of the nest itself." />
<p>What the above image is missing is the fact that the bird is now contained inside of it. So this is what it really looks like:</p>
<img src="https://sarasoueidan.com/assets/images/bird-nest-svg.png" style="border: 2px solid #eee;" alt="A visual respresentation of the nested svg element positioned on top of (or around) the nest, with the bird positioned inside of it." />
<p><strong>The grey border is the border representing the bounding box, and also the new coordinate system around the nest established by the <code>svg</code>.</strong></p>
<p>It’s important to note here that the bird is now positioned relative to the coordinate system of the inner <code><svg></code>. Notice how it is offset by some amount of pixels from both the top and left edges of the inner <code>svg</code>, just like it was positioned relative to the root <code>svg</code>. That is fine for now. We will need to get rid of that space to get a finer control over the position of the bird. But we’ll get to that shortly.</p>
<p>Another thing to note is that since the inner <code>svg</code> has an explicit height and width which are equal to the height and width of the nest’s bounding box, the bird’s feet get cut off at the bottom due to the way it is positioned. If you have other, more or different elements in your own projects, those might get cut off too. You definitely don’t want that. So to work around that, you need to explicitly set the <code>overflow</code> value to <code>visible</code> on the inner <code>svg</code>. This will ensure that the inner <code>svg</code> behaves only like a positioning context, not like a container that restricts its contents to a specific area visually.</p>
<p>Here is what the code looks like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>birds<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 3945.8 2400<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Bird <span class="token entity named-entity" title="&">&amp;</span> Nest<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>nest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">...</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- The ID I'm giving this SVG is just for demonstration purposes --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>698<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1219<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1055<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>641<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 1055 641<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">overflow</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>coord-sys<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">...</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Just like with the root <code>svg</code>, the <code>viewBox</code> value of the inner SVG <code>svg#coord-sys</code> is determined by its dimensions.</p>
<p>Next up, we need to position the bird inside the new coordinate system. I won’t refer to the inner <code>svg</code> as “inner svg” anymore—I’ll be referring to it as <code>svg#coord-sys</code>.</p>
<p>Since we will be positioning the bird inside the <code>svg#coord-sys</code>, we need to be able to specify a position for <em>the group of elements forming this bird</em>. After all, the bird is not made up of one element only—it is a group of shapes. And so we need to position <em>a group of elements</em>, not just one element. The group of elements forming the bird is wrapped in a group (<code><g></code>) element.</p>
<p>But the problem is: the <code><g></code> element does not have <code>x</code> and <code>y</code> attributes. So we can’t simply move it to a specific position like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>Usually, to move a group of elements around inside an SVG, we use SVG or CSS transform functions (translation transformation, most of the time). You can use transforms to move the group around, sure. But that would negate the whole idea we’re trying to achieve and would make the new coordinate system useless. After all, we could have used transforms to position the bird close to the nest inside the root <code>svg</code> without having to create a new coordinate system.</p>
<p>What we want is to mimic the way elements are positioned in CSS, <em>relative to each other</em>. So to say “move this group of elements to the position (x, y) inside this particular positioning context”.</p>
<p>Since <code><g></code> does not have <code>x</code> and <code>y</code> attributes, we’re going to substitute it with <i>another</i> <code><svg></code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>birds<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 3945.8 2400<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Bird <span class="token entity named-entity" title="&">&amp;</span> Nest<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>698<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1219<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1055<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>641<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 1055 641<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">overflow</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>svg</code> wrapping the bird has an ID <code>bird</code>. This SVG, as opposed to its ancestor, will only serve as a container and, even though it does create a new coordinate system, we won’t be using that system. Using this <code>svg</code>, we can now move the bird around inside the new coordinate system established around (on top of) the nest.</p>
<p>At this point, it is best to get rid of the white offset space around the bird. The innermost <code>svg#bird</code> has the same dimensions and <code>viewBox</code> as its wrapping <code>svg#coord-sys</code>; which means that in order to move the bird around, we need to take this white space into account. So if we want to move the bird to position it at the top left corner of the system, we won’t be able to simply say set x and y to zero—we will need to use a negative offset in both directions to achieve it. That’s not practical. We would also need to take this offset into account wherever and however we want to position the bird later.</p>
<p><small>At this point, you need to be not only familiar but also comfortable with how the <code>viewBox</code> works. I’m going to assume you are. If you’re not, pause here and go read <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">this article</a> first.</small></p>
<p>We will change the value of the <code>viewBox</code> of <code>svg#bird</code> to crop the white space out. (So we <em>are</em> going to use its coordinate system, but only a little bit.)</p>
<p>By default, a nested <code>svg</code> will occupy 100% the width and height of its container, unless you tell it otherwise.</p>
<p>So <code>svg``#bird</code> now has the exact same dimensions as that of the <code>svg``#coord-sys</code>. It’s the one with the pink border in the following image:</p>
<img src="https://sarasoueidan.com/assets/images/bird-nest-viewbox-shift.png" style="border: 2px solid #eee;" alt="A visual representation of the innermost svg showing its dimensions compared to its containing svg." />
<p>We don’t need the dimensions to be different in this example so we will leave them as they are.</p>
<p>The image above also shows the amount of white space by which the bird is shifted inside that <code>svg</code>. So in order to “unshift” it, we will change the value of the <code>viewBox</code> of the <code>svg#bird</code> to crop that white space out.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>birds<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 3945.8 2400<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>698<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1219<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1055<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>641<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">overflow</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>150 230 1055 641<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">...</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>That will shift the bird so that it is positioned at the top left of the coordinate system. I’m unfocusing <code>svg#bird</code> in the following image, so only the nest’s coordinate system is still shown, and the new position of the bird inside of it:</p>
<img src="https://sarasoueidan.com/assets/images/bird-nest-cropped.png" style="border: 2px solid #eee;" alt="The bird is now positioned at the top left corner of the newly established coordinate system." />
<p>So now that the bird is positioned at the top left of its wrapper, we can move it around and get the expected result every time. For example, if we were to move the bird by 50% in both directions:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">overflow</span><span class="token punctuation">:</span> visible</span><span class="token punctuation">"</span></span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>150 230 1055 641<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>We would get the following result:</p>
<img src="https://sarasoueidan.com/assets/images/bird-nest-position.png" style="border: 2px solid #eee;" alt="The bird positioned at 50% by 50% across the newly created cooridnate system." />
<p>With this setup, we can now move the bird around inside the nest’s coordinate system just like we would move an HTML element inside another one in CSS. Both relative and absolute position values work here too.</p>
<p>Pretty nice, huh? This is possibly the closest we can (currently) get to relative positioning in SVG today.</p>
<p>Granted, to get here is not the simplest process, but once you’ve got a good grasp of how SVG coordinate systems and the <code>viewBox</code> work, it’s hopefully not so complicated.</p>
<p>Here is a live demo of the above bird and nest, with the position of the bird set so that it stands at the edge of its nest:</p>
<p data-height="300" data-theme-id="3617" data-slug-hash="6f00a9b23653395afdd1c009d8ad6961" data-default-tab="html,result" data-user="SaraSoueidan" data-embed-version="2" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/6f00a9b23653395afdd1c009d8ad6961/">[Article Demo] Relative Positioning in SVG</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://assets.codepen.io/assets/embed/ei.js"></script>
<h4> Final Words </h4>
<p>The example used in this article is a very specific example and is, admittedly, not the most practical use case of all times. Your use case(s) are likely to be entirely different. You might be working with a very different SVG where you may not even need to do any viewBox cropping at all. If you create your SVG yourself, you can position your element (e.g. the bird in our case) at the top left of the SVG canvas, so that when you wrap it in another <code>svg</code>, it would also be positioned at the top left, and you wouldn’t have to do any cropping at all. I left this example slightly more complex just so we can cover more scenarios. (And because I was a little lazy to edit the SVG in Illustrator after having written half of this article. But I keep wanting to deny that.)</p>
<p>But the takeaway is <em>how</em> to mimic relative positioning using nested <code>svg</code>s. Whether you use one level, two levels, or more, the concepts are the same.</p>
<p>You might find this technique useful for positioning SVG UI elements relative to each other. Or maybe relative positioning in dynamically created SVGs. Your imagination is the limit.</p>
<p>I hope you found this article useful. Thank you for reading!</p>
Making the Switch Away from Icon Fonts to SVG: Converting Font Icons to SVG
2016-04-25T00:00:00Z
https://sarasoueidan.com/blog/icon-fonts-to-svg/
<p class="deck">
If you’re reading this article, then I can probably assume you’ve already decided to switch from using fonts for icons to an SVG icon system. Or maybe you're pondering the idea and want to get an overview of how that would be done and whether or not it's worth it. Either way, this post is here to help you with that.
</p>
<p>If you’re not already convinced as to why SVG is a better icon system, then I highly recommend reading <a href="https://css-tricks.com/icon-fonts-vs-svg/">this article</a>—a cagematch-style comparison between icon fonts and inline SVG for icon systems. Many companies and organizations including Github have already made the switch to SVG, and have written great articles explaining why they found SVG to be a better alternative. I’ve listed some articles at the end of this post for further reading.</p>
<h4 id="making-the-switch" tabindex="-1">Making the Switch</h4>
<h5 id="1.-grab-your-icon-fonts-files." tabindex="-1">1. Grab your icon fonts files.</h5>
<p>Font icons are font glyphs. They’re part of a web font and are thus defined in web font files and formats.</p>
<p>I don’t usually use icon fonts, so for the sake of demonstration, I headed to <a href="http://fontello.com/">Fontello.com</a>—an online icon font generator—to create an icon font that I can work with for this blog post. I picked a few icons and then generated an icon font and downloaded it.</p>
<img src="https://sarasoueidan.com/assets/images/fontello-icons.gif" />
<p>Fontello generates a folder containing a demo page showing you how to display the icons on your own page and what class names to use.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fontello-files.png" />
<figcaption>The files generated by Fontello.</figcaption>
</figure>
<p>Among the generated files is the actual font used to define the icons. The font files are available inside a <code>font</code> folder.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fontello-fonts.png" />
<figcaption>The font file formats generated by Fontello.</figcaption>
</figure>
<p>These are the files you need to proceed. We’re going to be using these files to “extract” the icons and convert them to SVG.</p>
<h5 id="2.-choose-your-tool." tabindex="-1">2. Choose your tool.</h5>
<p>To convert the icons to SVG, we can use one of the following tools:</p>
<ul>
<li><strong><a href="https://github.com/bpierre/fontello-svg">fontello-svg</a></strong>: “a command-line tool to generate the SVG versions of a Fontello icon set, with a corresponding CSS file.”</li>
<li><strong><a href="https://www.npmjs.com/package/font-blast">font-blast</a></strong>: “You can use font-blast to extract icons from any icon font - Font Awesome, Foundation, anything from Fontello etc.”</li>
<li><strong><a href="https://icomoon.io/app/">Icomoon app</a></strong>: a web app for generating and creating icon sets in both SVG and icon font formats.</li>
</ul>
<p>I’m sure there might be more tools to do this, but these are the ones I know about.</p>
<p>I’m going to be using <strong>Icomoon</strong> and <strong>font-blast</strong> in this article because they’re general tools that can be used with any font and are not restricted to just one. Both <strong>fontello-svg</strong> and <strong>font-blast</strong> are used pretty much the same way, and you can find extra information about <strong>fontello-svg</strong> in the Github repository’s Readme.</p>
<h5 id="3.-convert-the-font-icons-to-svg-icons." tabindex="-1">3. Convert the font icons to SVG icons.</h5>
<h6 id="3.1.-using-icomoon" tabindex="-1">3.1. Using Icomoon</h6>
<p>To convert the font icons to SVG icons using Icomoon, we first need to upload them.</p>
<img src="https://sarasoueidan.com/assets/images/icomoon-upload.gif" />
<p>Your font icons will be available as an icon set in the app. The next steps are the same steps you would take if you were choosing from the set of already-available icons on the page:</p>
<ul>
<li>Select the icons you want to download as SVG.</li>
<li>Click the <strong><em>Generate SVG & More</em></strong> button.</li>
</ul>
<img src="https://sarasoueidan.com/assets/images/icomoon-download.gif" />
<p>Like Fontello, Icomoon generates a folder containing the icons you generated, along with a demo page showing you how they can be used on your own pages.</p>
<img src="https://sarasoueidan.com/assets/images/icomoon-files.png" />
<p>The icons you’ve converted to SVG are available in the <strong><em>SVG</em></strong> folder.</p>
<img src="https://sarasoueidan.com/assets/images/icomoon-svg-icons.png" />
<p>There is one SVG file for every icon. The set is ready to be embedded in your page.</p>
<p>But before embedding the icons, you might want to sprite them. That is, create one SVG sprite that contains all of the icons, and then use that sprite to display each icon at a time, wherever needed on the page. Icomoon conveniently generates an SVG sprite (<em><strong>symbol-defs.svg</strong></em>) for you along with a polyfill (<em><strong>svgxuse.js</strong></em>) for older browsers that don’t support external sprite references.</p>
<h6 id="3.2.-using-font-blast" tabindex="-1">3.2. Using font-blast</h6>
<p>To convert the font icons to SVG icons using <strong>font-blast</strong> you need to first install font-blast using npm via your terminal.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">$ npm install font<span class="token operator">-</span>blast <span class="token operator">-</span>g</span></code></pre>
<p>The <code>-g</code> flag (short for <code>global</code>) ensures that you can run the script anywhere on your computer, regardless of the installation root.</p>
<p>As mentioned in the font-blast documentation, “You can generate icons from the command line by called the script with two parameters: the SVG file of the font, and the directory where inidivual icons should be placed”:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">$ font<span class="token operator">-</span>blast <span class="token punctuation">[</span>svg<span class="token operator">-</span>font<span class="token operator">-</span>file<span class="token punctuation">]</span> <span class="token punctuation">[</span>destination<span class="token operator">-</span>dir<span class="token punctuation">]</span></span></code></pre>
<p>My command looked like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">font<span class="token operator">-</span>blast <span class="token operator">/</span>Users<span class="token operator">/</span>Sara<span class="token operator">/</span>Downloads<span class="token operator">/</span>fontello<span class="token operator">-</span>08cdd41f<span class="token operator">/</span>font<span class="token operator">/</span>fontello<span class="token punctuation">.</span>svg <span class="token operator">/</span>Users<span class="token operator">/</span>Sara<span class="token operator">/</span>Downloads<span class="token operator">/</span>fontello<span class="token operator">-</span>08cdd41f<span class="token operator">/</span>svg<span class="token operator">-</span>icons</span></code></pre>
<p><small>Tip: You can drag your folder into the terminal, which will sort of drop the path to that folder into the terminal, so you don’t have to manually write it or grab it and then copy-paste it.</small></p>
<p>Running the above command, font-blast retrieves the icons from the font files and creates an SVG icon for each one, and saves the result to the folder you specify in the command line. My terminal then looks like this:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/font-blast-command-result.png" />
<figcaption>The result of running the font-blast command in the terminal, letting us know how many icons were found and converted to SVG.</figcaption>
</figure>
<p>The <strong><em>svg-icons</em></strong> folder I chose for the generated files looks like this:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/font-blast-files.png" />
<figcaption>The files generated by font-blast show the source font file used to extract the icons and include a folder containing the generated SVG icons.</figcaption>
</figure>
<p>As you have guessed, the <strong><em>SVG</em></strong> folder contains the generated SVG icons:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/font-blast-svg-icons.png" />
<figcaption>The font-blast-generated SVG icons.</figcaption>
</figure>
<p>The icons are then ready to be embedded on your page.</p>
<h5 id="4.-sprite%2C-embed%2C-style%2C-animate%2C-have-fun!" tabindex="-1">4. Sprite, Embed, Style, Animate, Have fun!</h5>
<p>There are <strong>three</strong> main ways to create and use SVG sprites. I wrote an overview of these techniques <a href="https://24ways.org/2014/an-overview-of-svg-sprite-creation-techniques/">here</a>. We will explore each of these techniques in more detail in an upcoming article (or a series of articles), show the pros and cons of each one, and how to create and use the SVG sprites, so stay tuned.</p>
<p><small>You can <a href="https://sarasoueidan.com/rss.xml">subscribe to the RSS feed</a> of my blog to stay up-to-date with new articles. An e-mail newsletter is in the works too!</small></p>
<h5 id="recommended-reading%3A" tabindex="-1">Recommended Reading:</h5>
<ul>
<li><a href="https://css-tricks.com/icon-fonts-vs-svg/">Inline SVG vs Icon Fonts [CAGEMATCH]</a> — a <strong>must-read</strong> that clearly highlights the advantages of SVG and why it’s a superior icon system format.</li>
<li><a href="http://blog.cloudfour.com/seriously-dont-use-icon-fonts/">Seriously, Don’t Use Icon Fonts</a></li>
<li><a href="https://benfrain.com/seriously-use-icon-fonts/">Seriously, Use Icon Fonts</a> — I still recommend using SVG for icon systems, but it’s always good to read a different point of view.</li>
<li><a href="https://github.com/blog/2112-delivering-octicons-with-svg">Delivering Octicons with SVG</a> on the Github blog</li>
<li><a href="http://ianfeather.co.uk/ten-reasons-we-switched-from-an-icon-font-to-svg/">Ten reasons we switched from an icon font to SVG</a></li>
</ul>
SVG Style Inheritance and the ‘Flash Of Unstyled SVG’
2016-03-01T00:00:00Z
https://sarasoueidan.com/blog/svg-style-inheritance-and-FOUSVG/
<p class="deck">There are too few things not to like about SVG, and I don’t mean the things that browsers cause by incomplete or lack of certain features or buggy implementations. Yet you might sometimes get some unpredictable results that might frustrate you when working with SVG, if you don’t know the details of <em>how</em> certain features <em>should</em> behave and what to expect from them, as per the specifications. SVG presentation attributes come with a bit of their own behavior which might sometimes surprise you.</p>
<p>I’ve written and spoken quite a bit about styling and animating SVGs with CSS, and have included a list of resources to dive into the details at the end of the article. I’ve also touched on the subject of style inheritance and the CSS Cascade in SVG in both speaking and writing. However, the topic is worth its own blog post.</p>
<p>I’ve been meaning to write this article for a while now but have been kept preoccupied. But what prompted me to finally write it today is this tweet I saw yesterday:</p>
<blockquote><p>Issue: Oversized SVG icons when the page’s CSS fails to load <a href="https://t.co/FwkaBAzrAT">https://t.co/FwkaBAzrAT</a></p>— Web Platform Daily (@openwebdaily) <a href="https://twitter.com/openwebdaily/status/704380922892820481">February 29, 2016</a>
</blockquote>
<p>And I remembered that I have already mentioned the cause and solution to this issue in one of my talks as well as in the Smashing Book 5 chapter on SVG, but never in one of my articles, not even the article focused on making SVGs responsive with CSS. (See links at the bottom of the article.)</p>
<h3 id="so%2C-what%E2%80%99s-the-problem-again%3F" tabindex="-1">So, what’s the problem again?</h3>
<p>When CSS is disabled or fails to load for any reason—such as when you’re on lo-fi, most SVG images, especially inline ones, will scale and take up the entire viewport width, thus making the unstyled page look even more ‘broken’ than it already does.</p>
<p>I’ve come across this scaling issue quite a few times in the past when I visited <a href="http://codepen.io/">Codepen</a> on my fairly slow connection (which sometimes gets more than just ‘fairly’ slow). The responsive Codepen logo would take up the entire viewport area, thus blocking the content underneath it and forcing you to scroll down to read whatever comes after it.</p>
<p class="size-2x">This—allow me to call it—<em>Flash of Unstyled SVGs (FOUSVG)</em> is caused by the lack of <code>width</code> and <code>height</code> attributes on the <code><svg></code> element. </p>
<h3 id="but-the-svgs-are-meant-to-be-responsive%E2%80%A6" tabindex="-1">But the SVGs are meant to be responsive…</h3>
<p>So why would you want to set explicit dimensions on your <code><svg></code>when what you’re really trying to do is make the SVG fluid, right?</p>
<p>Right.</p>
<p>Ideally, we should make our SVGs responsive while also catering for any worst case scenarios where our styles are ignored or simply not applied for any reason.</p>
<p><strong>By taking advantage of the SVG style inheritance tree</strong>, we <em>can</em> make our SVGs responsive while simultaneously planning for worst case scenarios, and providing a decent, less broken, fallback.</p>
<h3 id="the-how" tabindex="-1">The How</h3>
<p>In order to avoid the no-CSS scaling issue, all you need to do is <em>not</em> remove the <code>width</code> and <code>height</code> attributes from the SVG.</p>
<h4 id="1.-keep-the-width-and-height-attributes" tabindex="-1">1. Keep the <code>width</code> and <code>height</code> attributes</h4>
<p>This means that, if you’re creating the SVG yourself in, say, Adobe Illustrator, you should <strong>avoid checking the ‘Responsive’ option in the Export panel(s)</strong>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/ai-new-export-panel.jpg" />
<figcaption>
The ‘Responsive’ option in the new Illustrator SVG Export Options panel.
</figcaption>
</figure>
<figure>
<img src="https://sarasoueidan.com/assets/images/export-options-responsive.jpg" />
<figcaption>
The ‘Responsive’ option in the older Illustrator SVG Export Options panel.
</figcaption>
</figure>
<p>It’s very tempting to check that option, because you do want your SVGs to be responsive, after all, but you shouldn’t check it unless what you need is for the SVG to simply occupy the entire width on the screen—like when it’s <em>supposed</em> to be viewport-width, for example. <small>(I’ve embedded an SVG in my current client project and I <em>wanted it</em> to occupy the full page width, so I safely removed the attributes.)</small></p>
<p class="size-2x">Unchecking the ‘Responsive’ option will ensure that Illustrator will export the SVG and preserve the dimensions it has.</p>
<h4 id="2.-specify-your-desired-width-and-height-values-in-the-css" tabindex="-1">2. Specify your desired <code>width</code> and <code>height</code> values in the CSS</h4>
<p>You want your SVG to scale—be fluid—and fill out its container’s width?</p>
<p>No problem. Tell the browser you want that to happen by specifying the dimensions specified in the attributes above, using CSS properties:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>or maybe something like</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* maybe even... */</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>which will restrict the dimensions of an SVG icon, for example, and <strong>scale it in proportion to the text</strong> it is inline with.</p>
<p>Specifying your desired width and height in the CSS will make sure the width and height attributes no longer restrict the dimensions of the SVG when the CSS is successfully loaded and applied.</p>
<p class="size-2x">If the CSS <em>does</em> fail to load, the browser will use the values provided in the attributes as fallback, thus preventing the excessive scaling of the SVG.</p>
<h3 id="the-why%3A-why-does-the-above-technique-work%3F" tabindex="-1">The Why: Why does the above technique work?</h3>
<p>SVG presentation attributes are special style properties—a shorthand for setting a CSS property on an SVG node. For this reason, it only makes sense that SVG presentation attributes would contribute to the style cascade.</p>
<p>When I got started with SVG, I expected presentation attributes to be more specific than any other style declaration. It made sense to me that the “closer” a style is to a node, the more specific it is. For example, inline <code><style></code>s are more specific than external styles, an inline <code>style</code> attribute is more specific than <code><style></code> ‘islands’, so I thought it made sense for presentation attributes to be the most specific form of styles. But I was wrong.</p>
<p><a href="https://twitter.com/tabatkins/status/704392579895263233">Just like HTML presentational attributes</a>, SVG attributes count as low-level author style sheets and are overridden by any other style definitions: external style sheets, document style sheets and inline styles.</p>
<blockquote class="pull-quote">
SVG attributes count as low-level “author style sheets” and are overridden by any other style definitions: external style sheets, document style sheets and inline styles.
</blockquote>
<p>This makes it possible to provide a fallback for when the CSS styles are not available, so the SVGs still look good in their ‘worst’ case.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-preso-attrs-cascade.jpg" alt="Table showing the position of presentation attributes in the CSS Cascade" />
<figcaption>
The order of styles in the cascade. Styles lower in the diagram override those above them. Presentation attribute styles are overridden by all other styles except for those specific to the user agent.
</figcaption>
</figure>
<h4 id="tip%3A-you-can-use-presentation-attributes-to-provide-fallback-for-more-than-svg-scaling" tabindex="-1">Tip: You can use presentation attributes to provide fallback for more than SVG scaling</h4>
<p>If you have an SVG you’re styling and animating using CSS properties, you may start by ditching the presentation attributes altogether—we do have an option to do just that in Illustrator’s Export panel, by choosing CSS Properties over Presentation Attributes. That will lead to all attributes being exported as CSS properties, if they <em>can</em> be set as CSS properties.</p>
<p><small>You see, only a subset of all CSS properties may be set by SVG attributes, and vice versa. The SVG specification lists the <a href="https://www.w3.org/TR/SVG/propidx.html">SVG attributes that may be set as CSS properties</a>. Some of these attributes are shared with CSS, such as <code>opacity</code> and <code>transform</code>, among others, while some are not, such as <code>fill</code>, stroke and <code>stroke-width</code>, among others.</small></p>
<p>In SVG 2, this list will include <code>x</code>, <code>y</code>, <code>width</code>, <code>height</code>, <code>cx</code>, <code>cy</code> and a few other presentation attributes that were not possible to set via CSS in SVG 1.1. The new list of attributes <a href="https://www.w3.org/TR/SVG2/styling.html#SVGStylingProperties">can be found in the SVG 2 specification</a>.</p>
<p><small>Some of the ‘new’ CSS properties for SVG are already implemented and available in Chrome today!</small></p>
<p>There is a benefit to keeping the presentation attributes inside the SVG as well, not just on the root element. These benefits are highlighted the most when you attempt to style the contents of <code><use></code>d elements in SVG.</p>
<p>When you re-<code>use</code> an element, the contents of that element are copied, as is, into wherever you place the <code><use></code> in the page. But you may want to re-use an element mutliple times and style each occurance differently. You can do that by leveraging CSS Custom Properties (a.k.a. CSS Variables).</p>
<p>When ‘theming’ multiple <code><use></code> elements with CSS custom properties, it is recommended to always keep the presentation attributes that provide the default styles for the reused SVG, so that the image is styled both when the CSS fails to load and/or when the SVG is viewed in browsers that don’t yet support custom properties. Without the presentation attributes, most styles—such as <code>fill</code> and <code>stroke</code>—will default to black, which is probably not what you want.</p>
<h3 id="weird-svg-scaling-gotchas-to-be-aware-of" tabindex="-1">Weird SVG scaling gotchas to be aware of</h3>
<p><strong>Depending on how you embed your SVG</strong>, browsers will generally default to a 300px by 150px size, which is <a href="https://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">the default size for replaced elements in CSS</a>. This is the default size you get for <code><iframe></code>s as well, for example.</p>
<p>If the dimensions of the SVG are not specified, or if they are explicitly set to <code>auto</code>, the browsers will default to the 300 by 150 pixels dimension. If either dimension is set to <code>auto</code> (instead of 100% as mentioned earlier), the browser will cancel out the value set in the presentation attributes and will default to one of the default height and width values.</p>
<p>Well, kind of…</p>
<p>Again, It Depends™.</p>
<p>You see, the browser behavior depends on how you embed your SVG. If you embed the SVG using <code><iframe></code>, you get the 300x150 as a default. If you embed the SVG inline in the document (using <code><svg></code>), most browsers will scale it to the width of its container and will scale the height proportionally to preserve the SVG’s aspect ratio, while Internet Explorer will scale the width <strong>but not the height</strong>, so you need to explicitly tell it to scale the height as well.</p>
<p><small>Fun fact: IE will scale the height of the SVG if you give it an explicit <em>width</em> value of 100%. Crazy, but true.</small></p>
<p>I’ve written more about the rendering of different SVG embed techniques in the ‘Making SVGs Responsive with CSS’ article linked at the bottom of the article, along with ways to ensure the SVG is ‘responsified’ across all browsers.</p>
<p>Amelia Bellamy-Royds has also gotten into more detail about the browser scaling techniques in her article ‘How to Scale SVG’ on CSS-Tricks.</p>
<h3 id="scaling-tip%3A-never-drop-the-viewbox" tabindex="-1">Scaling Tip: Never drop the <code>viewBox</code></h3>
<p>Ever.</p>
<p>This is possibly the most important thing you need to be aware of when attempting to scale SVG: you <strong><em>need</em></strong> the SVG <code>viewBox</code> to properly scale SVG images.</p>
<blockquote class="pull-quote">
you <em><strong>need</strong></em> the SVG <code>viewBox</code> to properly scale SVG images.
</blockquote>
<p><strong>Do <em>not</em> remove it.</strong></p>
<p class="note">
After reading this artilce, developer David Bushell shared his own experience highlighting the importance of the <code>viewBox</code> to make sure SVG images are rendered as expected. You can check his post out <a href="http://dbushell.com/2016/03/01/be-careful-with-your-viewbox/">here</a>.
</p>
<p><small></small></p>
<h3 id="wrapping-up" tabindex="-1">Wrapping Up</h3>
<p>Having explicit, non-percentage <code>width</code> and <code>height</code> values set on an SVG not only helps with fixing FOUSVG issues, but it also helps with other scaling problems, especially when the SVG is used as a background image in CSS. Internet Explorer sometimes refuses to scale the image properly if it doesn’t have its aspect ratio specified with the <code>width</code> and <code>height</code> attributes. I’ve had this happen even with non-background images recently as well. Quite honestly, I don’t know the details of why or when exactly this happens, but I do know that we can avoid it by having these attributes available anyway.</p>
<p>The <code>viewBox</code> is even more important than the width and height, so always make sure you keep it there. You can learn all there is to know about how the <code>viewBox</code> works in <a href="https://sarasoueidan.com/blog/svg-coordinate-systems/">this article</a>. It is a very powerful attribute that is definitely worth taking the time to master.</p>
<h3 id="further-reading" tabindex="-1">Further Reading</h3>
<ul>
<li>[Slides] <a href="http://slides.com/sarasoueidan/styling-animating-svgs-with-css#/">Styling and Animating SVGs with CSS</a> – my talk from 2014, given at CSSConf and CSSConf EU.</li>
<li>[Article] <a href="https://www.smashingmagazine.com/2014/11/styling-and-animating-svgs-with-css/">Styling and Animating SVGs with CSS</a> — sort of a talk transcript, published on Smashing Magazine</li>
<li><a href="http://tympanus.net/codrops/2014/08/19/making-svgs-responsive-with-css/">Making SVGs Responsive with CSS</a> — article published on Codrops</li>
<li><a href="https://css-tricks.com/scale-svg/">How to Scale SVG</a> — by Amelia Bellamy-Royds</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Scaling_of_SVG_backgrounds">Scaling of SVG Backgrounds</a> on MDN</li>
<li><a href="http://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/">Styling the Contents of SVG <code><use></code> with CSS</a> on Codrops</li>
<li><a href="https://sarasoueidan.com/blog/svg-coordinate-systems/">Understanding SVG Coordinate Systems and Transformations (Part 1): The viewport, <code>viewBox</code> and <code>preserveAspectRatio</code> Attributes</a></li>
</ul>
<hr />
<p>I hope you found this article useful. Thank you for reading!</p>
2015 In Review
2016-01-06T00:00:00Z
https://sarasoueidan.com/blog/2015-in-review/
<p><i>Happy new year!</i> 🌟<br /> OK, I felt awkward starting a blog post that way—I confess. ☺️ But this is also an unusual post—not an article about CSS or SVG, and not a lengthy tutorial about web development. It is also the quickest post I ever wrote.</p>
<p>This is my first time writing a year in review kind of post. A lot of great stuff has happened in 2015 that I thought are worth documenting and have a look back at a few years from now. I’m grateful and thank God for every one of these things and everything else that has happened. And since a photo is worth a thousand words, I’ll start with a preview of the highlights in a few pictures, followed by sort of an alt text for each one, and then a little more:</p>
<style>
img {
width: 100%;
display: block;
float: left;
-webkit-filter: grayscale(1);
filter: grayscale(1);
}
img.nofilter {
-webkit-filter: none;
filter: none;
}
img.half {
width: 50%;
}
img.third{
width: 33.33%;
}
</style>
<figure class="wider">
<img src="https://sarasoueidan.com/assets/images/2015-02.jpg" class="half" />
<img src="https://sarasoueidan.com/assets/images/2015-08.jpg" class="half nofilter" />
<img src="https://sarasoueidan.com/assets/images/2015-01.jpg" class="third nofilter" />
<img src="https://sarasoueidan.com/assets/images/2015-04.jpg" class="third" />
<img src="https://sarasoueidan.com/assets/images/2015-05.jpg" class="third" />
<img src="https://sarasoueidan.com/assets/images/2015-07.png" class="half nofilter" />
<img src="https://sarasoueidan.com/assets/images/2015-06.jpg" class="half" />
<figcaption style="text-align: left; clear: both; padding: 2em 1em;">
Some of my favourite work-related highlights of 2015. From the top left:
<ol>
<li>Delivering the opening keynote at CSSDevConf 2015. Photo by Chris Casciano.</li>
<li>My 2015 speaker badges for the conferences: CSSConf AU, Smashing Conf LA, btconf Duesseldorf, Microsoft Edge Summit, Generate London, Velocity NY, CSSDevConf, View Source conf, ffconf.</li>
<li>Me accepting the Developer of the Year award at the <i>net</i> awards ceremony. Photo by and copyright of <i>net</i> magazine. (I think I look sleep deprived in that photo, but it was already two hours past my bed time, so, there’s that.)</li>
<li>Photo of the net award. (These things are very difficult to take proper photos of! ☝)</li>
<li>Photo of the O’Reilly Web Platform Award I received in April. (I took like ten photos till I finally got a decent one. 😅)</li>
<li>Photo of the first page of the “Mastering SVG for Responsive Web Design” chapter in the Smashing Book 5.</li>
<li>Me giving a workshop at Smashing conf LA. Photo by Marc Thiele.</li>
</ol>
</figcaption>
</figure>
<ol>
<li>In 2014, Manoela Ilic asked me to write <strong>a CSS Reference for Codrops</strong>. In February 2015, we finally <a href="https://sarasoueidan.com/blog/codrops-css-reference">released the 8–months worth of work & writing</a> to the world.</li>
<li>This year, I also <strong>got published for the first time in a real book</strong>—<a href="http://www.smashingmagazine.com/2015/03/31/real-life-responsive-web-design-smashing-book-5/">the Smashing Book 5</a>, which I contributed to with a very extensive article about using SVG in a RWD workflow.</li>
<li>Back in April, I <strong>won an O’Reilly Web Platform award</strong>. I had never heard of the award before I was informed about winning it, and so I had no clue that I was even nominated. That <a href="https://sarasoueidan.com/blog/oreilly-web-platform-award/">came as such a beautiful surprise</a>!</li>
<li>Back in June, I was <strong>nominated and shortlisted for two <em>net</em> awards</strong>: <i>Developer of the Year</i> and <i>Outstanding Contribution</i>.</li>
<li>In September, I <strong>won the <em>Developer of the Year</em> <em>net</em> award</strong>. This was one of the most awesome things that have happened since I started doing what I do on the web, so thank you <em>so much</em> for each and every one of you who nominated me and voted for me. I am humbled and extremely proud! <em>You are awesome!</em> 💜</li>
<li>I published 23 articles. That’s an average of 2 articles per month, which I think is good. I like to take my time with the articles I write, as opposed to try to publish as much as possible during a specific period of time.</li>
<li>I <strong>spoke at 9 conferences worldwide</strong>—in the US, UK, EU and AU, including the first <a href="https://channel9.msdn.com/events/WebPlatformSummit/2015">Microsoft Edge Summit</a>, and <strong>gave my first workshop</strong> about SVG at Smashing Conf in LA.
<small>(A lot of people ask me for videos of these talks sometimes. You can find a list of them—in chronological order— on my <a href="https://sarasoueidan.com/speaking">speaking page</a>.)</small></li>
<li>I <strong>gave two conference keynotes</strong>, one of which was the first day opening keynote—at <em>CSSDevConf</em> on board the Queen Mary (a boat!!) in California.</li>
</ol>
<p>These points pretty much sum up the biggest highlights of the year, and the ones like to remember it with.</p>
<p>Of course, many things happened on a personal life level, but those don’t belong here, so I will spare you the list of them.</p>
<hr />
<p>This one is on the weirder kind of highlights side, but definitely one of the things I remember 2015 with: I finally got a British visa! Those of you who have been following me in the last couple of years are already familiar with my visa <a href="http://beyondtellerrand.com/blog/why-you-never-should-give-up">stories</a>. I apply for visas a lot, given the passport I hold (Lebanese) and then the places I speak in. I need to apply for a visa for most conferences I want to speak at, which sucks but is unfortunately necessary.</p>
<p>But one visa application that was particularly painful for me was the British visa, given how unfair the result was, simply because the person responsible for granting the visas was “not satisfied with my intentions in the UK”. 😒🙄
I decided to give it another shot last year for Generate London and ffconf, as well as to attend the <em>net</em> awards ceremony. After having showed evidence of my 10 previous speaking appearances at that time, the UK Visa & Immigration was <i>finally</i> convinced, and they granted me the visa. Woo.</p>
<hr />
<p>Of course, 2015 wasn’t all rainbows and unicorns. I failed to do some of the things I was hoping to do, too.</p>
<p>Possibly the number one thing I regret to say that I didn’t do was to keep maintaining and updating the Codrops CSS Reference with new entries as I intended to.</p>
<p>With client development projects taking priority over all other work, and then the many speaking and writing commitments I had, it was very hard to find enough time to do this. Maintaining the reference requires way more time than I could possibly devote for it.</p>
<p>As such, I think it’s an appropriate time to officially announce that I am no longer the maintainer of the reference. (More news and updates about this will be shared on and by Codrops some time in the near future, so no elaboration is necessary at this point.) The reference is one those things I’m most proud of, and I know it will be well taken care of.</p>
<p>In 2015, I also managed to strain my wrist which is still not healed today. I can’t exercise like I used to, I don’t have enough strength in my left hand to do things that I would normally do with ease, and, well, it’s just frustrating. The pain comes and goes but it’s <i>just not healed yet</i>, which means I still have to see a doctor (again) and keep away from any form of physical activity that adds to the strain of it.</p>
<p>On the bright side: this means that I take longer breaks from work, which is something to add to the list of things I failed to do. I overworked and pushed myself to the limits, and reached the boundaries of a burnout mutliple times last year. This is one of those things I am currently working on and making sure it doesn’t happen again. Saying ‘No’ to stuff helps a lot, by the way. <em>#protip</em></p>
<hr />
<p>To keep this post short, <strong>I will skip the 2016 resolutions list</strong>. Partly because it’s not finalized yet, mainly because my goals aren’t really year-specific, so I’m not sure they count as “2016 resolutions”, and additionally because <strong>I’ve got a couple more announcements and updates coming</strong> that deserve their own blog posts. So, stay tuned!</p>
<p>I want my web site to be home for most (not all) of my articles and tutorials in the future, so make sure to subscribe to the RSS feed to stay updated with what comes next.</p>
<hr />
<p>So, that was my look back at 2015. I’m really excited to see what 2016 will bring. I don’t expect it to be all good, because I’m a realistic person, but I do always keep a positive eye on the future… or at least I try to.</p>
<p><em>What was <i>your</i> 2015 like? What have you accomplished and what are you most proud of? And what have you got planned for 2016?</em></p>
Animated SVG vs GIF [CAGEMATCH]
2015-11-24T00:00:00Z
https://sarasoueidan.com/blog/svg-vs-gif/
<p class="deck">SVG can do much more than display static images. Its animation capabilities are one of its most powerful features, giving it a distinctive advantage over all other image formats. They are one of many reasons that make SVG images better than raster images, including GIFs. But this, of course, only applies to images that are good candidates for SVG, such as:</p>
<ul>
<li>Logos</li>
<li>non-complex, vector-based illustrations,</li>
<li>user interface controls,</li>
<li>infographics,</li>
<li>and icons.</li>
</ul>
<p>Of course, if you have an image that is better suited for the raster format—such as a photograph or a very complex vector illustration (that would normally have a very big size as an SVG), then you should use a raster image format instead. Not only should the image be a good candidate for SVG, but SVG should also be a good candidate for the image. If the image size is much less as a PNG, for example, then you should use PNG, and serve different versions/resolutions of that image using <code>srcset</code>, or <code><picture></code>, depending on what you’re working on and trying to achieve.</p>
<blockquote class="pull-quote">
Not only should the image be a good candidate for SVG, but SVG should also be a good candidate for the image.
</blockquote>
<p>Generally speaking, the images listed above are usually perfect candidates for SVG. And if you’re going to animate any of those, creating your animations by animating the SVG code is the sensible way to go.</p>
<p>However, last week, a link popped up in my Twitter timeline that linked to a set of icons that are animated as GIFs.</p>
<p>The first thing that crossed my mind when I saw them was that they were perfect candidates for SVG and should be created as SVG images, not GIFs.</p>
<p>SVGs can indeed replace GIFs in many places, just like they can replace other raster image formats for candidates like those mentioned above. The ability to animate SVG images is what gives it this advantage and ability. And this applies to more than just animated icons.</p>
<p>So, here is why I think you should use SVG instead of GIFs whenever you can.</p>
<h3 id="image-quality" tabindex="-1">Image Quality</h3>
<p>The first advantage to using SVG over GIFs—or any image format, for that matter—is, unsurprisingly, SVG’s number one feature: resolution-independence. An SVG image will look super crisp on any screen resolution, no matter how much you scale it up. Whereas GIFs—a raster image format—do not. Try zooming in a page that contains a GIF image and watch the GIF become pixelated and its contents blurred.</p>
<p>For example, the following GIF recording of an SVG animation looks fine at this small size:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--animation-example.gif" alt="" />
<figcaption>
A GIF recording of the <a href="http://codepen.io/chrisgannon/pen/myZzJv">SVG Motion Trails demo</a> by Chris Gannon.
</figcaption>
</figure>
<p>Zooming into the page a few times will cause the image to be pixelated and the edges and curves of the elements inside to become jagged, as you can see in the image below:</p>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--animation-example-zoomed-in.png" alt="" />
<p>Whereas if you <a href="http://codepen.io/chrisgannon/pen/myZzJv">check the SVG demo out</a> and zoom into the page, the SVG content will remain crisp and clear no matter how much you zoom in.</p>
<p>To provide crisp images for high-resolution displays when you’re using a bitmap image format like GIF, you need to use <code><picture></code> or <code>srcset</code> and switch the images up for different contexts.</p>
<p>Of course, the higher the image resolution, the bigger the file size will be. With GIFs, the file size will end up ridiculously large; but we’ll get to that in a minute. Also, using a high-resolution GIF and serving it at a smaller size for mobiles is bad for performance. <strong>Don’t do it.</strong></p>
<p>When you create GIF-animated icons or images, their dimensions are fixed. Change the dimensions or zoom in and out of the page, and they’ll get pixelated. With SVG, size is free, and clarity is a constant. You can create a small SVG and have it scale up as much as needed without sacrificing image clarity.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">GIF, just like other image formats, are not resolution-independent, and will therefore look pixelated when scaled up or viewed on higher resolutions.</td>
<td class="win">
SVG is scalable and resolution-independent, and will look crisp clear on any screen resolution.
</td>
</tr>
</table>
<h3 id="colors-and-transparency" tabindex="-1">Colors and Transparency</h3>
<p>Perhaps the number one deal-breaker with GIFs is the way transparency is handled, especially when the image is displayed on a background other than a white background.</p>
<p>This is an issue that is most likely to emerge when using GIF icons (whether animated or not), since icons are usually created with transparent backgrounds.</p>
<p>For example, take the following circle with a stroke, created as both an SVG image (left) and a GIF with a transparent background (right). The problem is evident as soon as you look at the two images: the GIF circle has grey fringes around its stroke.</p>
<figure class="double" style="background-color: #003366;">
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--circle-on-transparent-background.svg" alt="" width="350px" />
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--circle-on-transparent-background.gif" alt="" width="350px" />
</figure>
<p>If you’re not reading this in the browser, the effect might not be visible to you because the figure styles might not be applied. Here is a screenshot showing the problem (on the right):</p>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--artefact.png" alt="" />
<p>This happens because transparency in GIF images is binary. This means that each pixel is either <em>on</em> or <em>off</em>; a pixel is either transparent or fully opaque. This, in turn, means that the transition between the foreground color and the background color is not as smooth, and results in artefacts caused by inadequate sampling frequency, commonly known as <em>aliasing</em>.</p>
<p>When a line is not completely straight, it causes some pixels (around the edges) to be partially transparent and partially opaque, so the software needs to figure out what color to use for those pixels. The halo effect “is caused by all the pixels which would have been > 50% opaque being fully opaque and carrying the bg color against which they were rasterized” (<a href="http://twitter.com/svgeesus/">Chris Lilley</a>). So this effect is usually a result of pixel contamination from the color of the background against which the image was composited against upon creation/saving in a graphics editor.</p>
<p>Aliasing is usually countered with <em>anti-aliasing</em>, but that is not as simple when transparency is binary:</p>
<blockquote>
<p>There is a <strong>severe interaction between anti-aliasing and binary transparency</strong>. Because the background colour of the image is mixed in with the foreground colours, simply replacing a single background colour with another is not enough to simulate transparency. There will be a whole host of shades which are <em>mixtures</em> of background and foreground colours [...]. The effect in this case is a white halo around objects, because the original image was anti-aliased to a white background colour.</p>
<cite>— <a href="http://twitter.com/svgeesus/">Chris Lilley</a> (<a href="http://www.w3.org/Conferences/WWW4/Papers/53/gq-trans.html">Source</a>)</cite>
</blockquote>
<p>The solution to this problem is variable transparency, commonly known as the alpha channel, which allows for varying degrees of transparency and hence a smoother transition between the foreground and background color, which is not what is available in GIF; thus, the halo effect problem. Images with the halo effect usually look best when used with white backgrounds; any other high-contrast background color will make the artefact visible.</p>
<p>I’m not quite sure if there is a way to work around this issue, but I’ve not yet come across a GIF with a transparent background and curved edges that did not have this problem. I’ve even seen rectangular shapes suffer from it as well.</p>
<p>If you want to use your image/icon on a non-white background—say, on a dark footer background, this alone could be a deal-breaker. But there are other reasons SVG is better than GIFs too, that we’ll cover in the next sections.</p>
<p><strong>Note:</strong> if you’re reading this article in a browser but still don’t see the fringes in the first image on a smaller screen, try zooming the page in to see the effect.</p>
<p>Why might you not be able to see the fringes on smaller sizes? The answer is: the browser smoothes the edges as a part of the image resize process.
Does this mean that you can utilize this to get rid of the fringes and still use a GIF? Yes, you can. But to do that, you have to use a GIF that is much bigger than the size you want to render it at, and then resize it. This also means that you will be serving your users images that are much bigger than they need, therefore taking up more of their bandwidth on mobile, as well as hurting the overall page size and performance. Please don’t do that.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">GIF images are capable of only binary transparency. This causes artefacts, known as the <em>halo effect</em> to show up whenever the image or icon is used on a non-white background. The higher the background color contrast with the image, the more visible the halo effect, which makes the icons practically unusable.</td>
<td class="win">
SVG images come with an alpha channel and do not suffer from any problems when they are used on different background colors.
</td>
</tr>
</table>
<h3 id="animation-techniques-%26-animation-performance" tabindex="-1">Animation Techniques & Animation Performance</h3>
<p><strong>You can animate SVGs using CSS, javaScript or SMIL</strong>, and each of them gives you a different level of control that you can take advantage of to create all kinds of animations on SVG elements.</p>
<p>There are no “techniques” to animate GIF images. They are animated by showing a series of images—one for each frame—sequentially, in a fixed manner, at a fixed pace. You know, the way GIFs just work. Granted, you can get creative with your icons before you turn them into GIFs and then “record” the animation and convert it into a GIF, but how good will it look? And how much control over the animation timing will you get afterwards? None.</p>
<p>Unless you make sure you have at least 60 frames—that is, 60 images—<em><strong>per second</strong></em> to create your GIF, the animation will not look smooth. Whereas with SVG, achieving smooth animations is much easier and simpler by taking advantage of browser optimizations.</p>
<p>A GIF has a bigger file size than PNG or JPEGs, and the longer the animation duration, the bigger the size will be. Now, what if your animation plays for at least 5 ot 6 seconds? What if it plays for much longer?</p>
<p>You get the picture.</p>
<p>Let’s look at a more specific yet minimal example. Below are two images: an animated SVG on the left, and an animated GIF on the right. The rectangle in both images changes color over the course of six seconds.</p>
<figure class="double">
<svg width="300" height="150" viewBox="0 0 300 150" xmlns="http://www.w3.org/2000/svg"><style>svg {width: 48%;}path { animation: loop 6s linear infinite; } @keyframes loop { to { fill: #009966; } }</style><path fill="#ff1493" d="M0 0h300v150h-300z"></path></svg>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--rectangle-animation.gif" alt="" width="300px" height="150px" />
<figcaption>The SVG image on the left and the GIF on the right.
</figcaption>
</figure>
<p>There are a couple of things to note here:</p>
<ul style="text-align: left;">
<li>The GIF animation looks smooth but if you look closely you will notice that the SVG rectangle is going through a wider range of colors as it transitions from the initial to the final color. <strong>The number of colors the GIF goes through is limited by its number of frames.</strong> In the above image, the GIF goes through 60 frames, i.e. 60 colors, whereas the SVG goes through the entire spectrum between the shade of pink used and the final green color.</li>
<li>For looping animations like this one, it is generally best to avoid the color jump shown in the above animation, and create the animation so that it reverses once it reaches the green color; that way, it will transition smoothly back to pink and then start the second round of animation from there too, avoiding that unsightly color jump.
<p> With CSS, you can reverse the animation using the <code>alternate</code> animation direction value. But with GIF, you will need to work on your number of frames and probably end up doubling it to make this happen; this will, of course, also increase the size of the image as well.
</p></li>
</ul>
<p>The sizes of the two images shown above are:</p>
<ul>
<li>GIF image size: <strong>21.23KB</strong></li>
<li>SVG image size: <strong>0.355KB</strong></li>
</ul>
<p>This is no trivial difference. But we all know we can optimize our images. So let’s do that.</p>
<p>Optimizing the SVG using the SVGO Drag-and-Drop GUI brings the SVG’s file size down to <strong>0.249KB</strong>.</p>
<p>To optimize the GIF, you can use one of the several GIF optimization tools online. I used <a href="http://ezgif.com/">ezgif.com</a> to optimize the above image. (Other tools also exist; <a href="http://www.lcdf.org/gifsicle/">gifsicle</a> is one of them.) The file size dropped down to <strong>19.91KB</strong>.</p>
<p>There are many options you can choose from when optimizing GIF images. I optimized the above image so that the number of frames remains intact, using Lossy GIF compression, which <q>can reduce animated GIF file size by 30%—50% at a cost of some dithering/noise.</q></p>
<p>You can also optimize it by removing every nth frame; that can reduce the file size even further, but at the cost of the animation not being smooth anymore. And in the case of an animation like the case at hand, removing frames will make the change in color be “jumpy” and noticeable.</p>
<p>Other optimization options are also available such as color reduction (which wouldn’t be suitable for our color-dependent animation here) and transparency reduction. You can learn more about these options on the <a href="http://ezgif.com/">ezgif.com</a> optimization page.</p>
<p>To recap: If you want your GIF animation to be smooth, you’re going to need more frames per second, and that will consequently increase the file size by a lot. Whereas with SVG, you’re likely to maintain a much smaller file size. The above example is minimal, and I’m sure there are better ones out there, but I wanted the most minimal example to show the difference between the two formats.</p>
<p>Even if you were animating the above rectangle using JavaScript or even a JavaScript framework—since animations on SVG don’t work in IE, for example, the file size of that framework combined with that of the SVG is still likely to be smaller or at least equal to the size of the GIF image size. For example, using <a href="http://greensock.com/">GreenSock</a>’s TweenLite, the size of the SVG with the library combined would be less than 13KB (which is still less than the size of the GIF), since TweenLite is 12KB minified. If you do end up with a size equal to that of the GIF, the other benefits of SVG will tip the scale and you will be getting more out of it.</p>
<p>Some other JavaScript libraries exist that focus on certain animation tasks at a time, and come in impressivly small file sizes (<5KB), such as <a href="https://github.com/lmgonzalves/segment/blob/gh-pages/dist/segment.min.js">Segment</a> which is used to animate SVG paths to create line drawing effects. Segment is 2.72KB minified. That’s not too shabby, is it?</p>
<p>There can be exceptions, so you should always test. But given the nature of GIFs and how they work, you will likely find that SVG is a better option in most cases.</p>
<p class="note">Note: SVG Performance is not at its absolute best today, but this will hopefully change in the future. IE/MS Edge offer the best SVG rendering performance among all browsers today. Despite that, SVG animations will still look better than GIF animations, especially when you're tackling long animations, because the file size of the GIF—assuming it’s recorded at 60fps—will have a negative impact on the overall page performance. Libraries like GreenSock also offer impressive performance as well. </p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">
<ol>
<li>GIF images are generally larger than SVG images. The more complex and longer the animation, the more frames are required to create it and therefore the bigger the file size and the more the negative impact on performance.</li>
<li>Unless GIF animation plays at 60fps, the animation is going to be jagged and not smooth. Also, the more the number of frames per second, the bigger the file size, especially for longer animations.</li>
</ol>
<p><strong>Result:</strong> There will be a compromise that needs to be made. Either the GIF animation is smooth and the overall file and page size and performance is negatively affected, or the GIF animation will suffer with less frames. One form of performance is risked in both scenarios.</p>
</td>
<td class="average">
<p>SVG images take advantage of the browser optimizations when animating elements. Even though browser performance on SVG elements is still not at its best, animation will still perform better without having to make page performance compromises.</p>
<p>
SVG file size is still very reasonable, if not very small, compared to GIFs, even when certain animation libraries might be required to create cross-browser animations.
</p>
</td>
</tr>
</table>
<h4 id="maintaining-%26-modifying-animations" tabindex="-1">Maintaining & Modifying Animations</h4>
<p>…is a pain if you are using GIFs. You will need to use a graphics editor such as Photoshop or Illustrator or After Effects, to name a few. And if you’re like me, then graphics editors are not where your skills shine, and you feel more at home when you make modifications in code, not in graphics editors.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--photoshop-frames.png" alt="" />
<figcaption>Screenshot of the animation timeline as created in Photoshop. The lower part shows a fraction of the frames created for the animation. Fore more complex animations, more frames are required. Also notice the layers panel.
<p><small>Thanks to my designer friend <a href="http://twitter.com/WalterStephanie">Stephanie Walter</a> for the PS animation tips.</small></p></figcaption>
</figure>
<p>What happens if you want to change your animation timing? or if you want to change the timing functions for one or multiple elements inside your image? or if you want to change the direction in which an element moves? What if you want to change the entire effect and have the elements in your image do something completely different?</p>
<p>You will need to recreate the image or icon all over again. Any change requires you to jump into the graphics editor and work with frames and a frame-based UI. That would be like torture to developers, and a Mission Impossible for those of us who don’t know our way around those editors enough to make these changes.</p>
<p>With SVG, making any kind of change to the animation(s) is only a few lines of code away.</p>
<p><strong>Conclusion (developer’s perspective)</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">Maintaining and modifying GIF animations requires re-creating the image or resorting to a frame-based graphics editor’s UI to do so, which is a problem for design-challenged developers.</td>
<td class="win">
SVG animations can be changed and controlled right inside the SVG code—or anywhere the animations are defined, usually using a few lines of code.
</td>
</tr>
</table>
<h3 id="file-size%2C-page-load-time-%26-performance" tabindex="-1">File Size, Page Load Time & Performance</h3>
<p>In the previous section, we focused on the performance of the animation itself. In this one, I want to shed some light on the page performance as a whole and how it is affected by the image format choice you make.</p>
<p>Fact: The bigger the file size, the more the negative impact on page load time and performance. With that in mind, let’s see how using SVG instead of GIFs can help improve the overall page load time by looking at a more practical, real-world example.</p>
<p>At my first SVG talk, 18 months ago, I mentioned how SVG can be used to replace animated GIFs and result in overall better page performance. In that talk, I provided a real-world example of a real-world web page that took advantage of what SVG has to offer and reaped the benefits: the <a href="http://sprout.is/">Sprout</a> homepage.</p>
<p>The Sprout homepage has two animated images that were initially created and displayed as GIFs. Two years ago, <a href="https://twitter.com/mfortress">Mike Fortress</a> wrote <a href="http://oak.is/thinking/animated-svgs/">an article on the Oak blog</a>, in which he explains how they recreated the animated GIFs, particularly the chart GIF (see image below) as an animated SVG image.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-vs-gif--sprout-chart.svg" alt="" />
<figcaption>
The SVG version of the chart used on the Sprout homepage and written about on the Oak article. <small>(All rights reserved by their owners.)</small>
<p>Note that the animation is created using SMIL so it will not be animating if you’re viewing it in Internet Explorer.</p>
</figcaption>
</figure>
<p>In his article, Mike shares some interesting insights on their new page performance as a result of making the switch to SVG:</p>
<blockquote>
<p>This chart, and one other animation on Sprout, were initially GIFs. By using animated SVGs instead of GIFs we were able to reduce our page size <strong>from 1.6 mb to 389 kb</strong>, and reduce our page load time <strong>from 8.75 s to 412 ms</strong>. That’s a huge difference.</p>
<cite>—Mike Fortress, <a href="http://oak.is/thinking/animated-svgs/">“Animated SVGs: Custom Easing and Timing”</a></cite>
</blockquote>
<p>A huge difference indeed.</p>
<p>The Sprout chart is a perfect candidate for SVG. There is no reason to animate it by converting the animation into a GIF recording, when SVG can bring so much more benefits.</p>
<p><a href="https://jakearchibald.com/">Jake Archibald</a> realizes the power of SVG animations too, and uses them to create and animate interactive illustrations to complement his articles. His <a href="https://jakearchibald.com/2014/offline-cookbook/">Offline Cookbook</a> article is an excellent example (and an excellent article, by the way). Could he have used GIFs to do that? Of course. But given the number of images he used, the GIFs could have easily increased the overall page size to a couple or few megabytes, with each GIF being at least hundreds of kilobytes in size; whereas <strong>the entire web page currently weighs at 128KB only with <em>all</em> the SVG images embedded inline</strong>, because <a href="https://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg">you can reuse elements in SVG</a>, so any repetitive elements will not only cause the entire page to <a href="http://calendar.perfplanet.com/2014/tips-for-optimising-svg-delivery-for-the-web/">gzip much, much better</a>, but for each page, the overall size of the SVGs becomes smaller.</p>
<p>Now <em>that</em> is impressive.</p>
<p>I will rest my case about page load and performance here. But it is still important to note that there <em>can</em> be exceptions. Again, in most cases, you’re likely going to find that SVG is better than a GIF, but you’ll always need to test anyway.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">GIF images are generally bigger in size than SVG images are with animations added to them. This negatively affects the overall page size, load times and performance.</td>
<td class="win">
SVG images can be used and reused, as well as gzipped better, making their file sizes generally smaller than those of GIFs, thus improving page load times and performance.
</td>
</tr>
</table>
<h3 id="browser-support" tabindex="-1">Browser Support</h3>
<p>Probably the only absolute advantage to GIFs over SVGs is browser support. GIFs work pretty much everywhere, while SVG support is less global. Even though we have many <a href="https://css-tricks.com/a-complete-guide-to-svg-fallbacks/">ways to provide fallback for non-supporting browsers</a>—and current browser support should not be hindering anyone from using SVG, the fallback images, if provided as PNG or JPG, are going to be static, animation-less.</p>
<p>Of course, you can always provide a GIF as a fallback to SVG, but the previously-mentioned considerations and disadvantages should be kept in mind.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="win">GIF images work pretty much everywhere.</td>
<td class="average">
SVG images have less global browser support, but they come with a lot of ways to provide fallback for non-supporting browsers.
</td>
</tr>
</table>
<h3 id="accessibility-concerns-(%23a11y)" tabindex="-1">Accessibility Concerns (#a11y)</h3>
<p>Move something on a page, or anywhere, for that matter, and you’ve instantly added a distraction—something that is sure to grab a user’s attention as soon as it starts moving. This is simply how the human brain works. This is also one of the reasons ad banners are so focused on—and built with—a strong focus on animation. This is also why animated ad banners are <strong>extremely annoying</strong>. They are distracting, especially when you’re trying to perform a task on a page that requires your entire attention, such as reading an article.</p>
<p>Now imagine a page with a set of animated icons (or images) that just won’t stop animating no matter what you do. We’re no longer talking about one or two images on a homepage or within an article here; we’re talking about UI elements and controls, and smaller icons that are likely to be present in multiple places on a page, and on multiple pages. Unless your icon is <em>supposed</em> to be inifnitely animation—for example, if it is a spinner animating during a user-inactive waiting phase, then it is likely to introduce a problem, and become more of an annoyance, than a “nice thing”.</p>
<p>As a matter of fact, for some people it can become more of an annoyance, as continuous motion can literally make some people feel ill.</p>
<p>In her article <a href="http://alistapart.com/article/designing-safer-web-animation-for-motion-sensitivity">“Designing Safer Web Animation For Motion Sensitivity”</a>, designer and web animation consultant Val Head discusses the effects of overused animation on the web on people with visually-triggered vestibular disorders (emphasis mine):</p>
<blockquote>
<p>It’s no secret that a lot of people consider scrolljacking and parallax effects annoying and overused. But what if motion does more than just annoy you? What if it also makes you ill?</p>
<p>That’s a reality that people with visually-triggered vestibular disorders have to deal with. As animated interfaces increasingly become the norm, more people have begun to notice that <strong>large-scale motion on screen can cause them dizziness, nausea, headaches, or worse. For some, the symptoms can last long after the animation is over.</strong> Yikes.</p>
</blockquote>
<p>Now imagine if the animation does <em>not</em> end… Double Yikes.</p>
<p>Val’s article explains the problem in more detail, as she gathers feedback from two people who actually have these problems and share their experience with animation in different examples.</p>
<p>One of the solutions that can help avoid these problems is to <a href="http://alistapart.com/article/designing-safer-web-animation-for-motion-sensitivity#section10">provide the user with the ability to control the animation</a> so that they can stop it when it gets disturbing.</p>
<p>With SVG, you can do that. You can fully control the animation and play it once or twice on page load—if you really need to have it play as soon as the user “enters” your page, and then fire it on hover for subsequent tweens, using nothing but a few lines of CSS or JavaScript. <strong>You do not need hundreds or thousands of lines of CSS or JavaScript to create an icon animation</strong>, unless your icon is a really complex scene with a lot of components that are animated inside of it. But I think that in that case, it does not count as an “icon” anymore, but more of a regular image.</p>
<p>You can go even as far as control playback, speed for each consequent tween, and much more, assuming, of course, you are using JavaScript to gain this level of control.</p>
<p>Or you can add a toggle to give the user the ability to stop an infinitely playing animation. You can’t do that with GIFs… unless you opt for replacing the GIF with a static image upon a certain toggle action.</p>
<p>Some might even argue that you could display a static version of the image—as a PNG for example, and then provide the GIF version on hover. But this comes with a few problems of its own:</p>
<ul>
<li>If the images are inline, you’ll need to replace these images using JavaScript. That action does not require any JavaScript if you are using SVG.</li>
<li>If the images are foreground images (embedded in the HTML using <code><img></code>), and you need to replace these images, you will end up with double the amount of HTTP requests for every image. And if they are background images inlined in the style sheet (which is not recommended), the images (especially the GIFs) will add to the size of the style sheet and therefore to the overall render-blocking time of the page.</li>
<li>If/when you switch image sources on hover, there is a noticable flash between the first and the second image on slower connections. My connection is slow; sometimes 3G-slow, and I have yet to remember a time when an image was replaced with another one on hover, viewport resize, or whatever, and not have seen that flash. This situation gets even worse when the second image (GIF loaded on hover) is fairly big in size—there will be a flash, followed by a slow, janky animation of the GIF while it loads completely. That’s never attractive.</li>
</ul>
<p>So, yes, you can switch image sources to control if or when the GIF animation plays, but you’re losing the finer control over the GIF and affecting the user’s experience with the UI.</p>
<p>You are also able to control how many times the animation plays in the GIF—which is pretty cool, but that means that the animation will play only <strong><em>n</em></strong> number of times. And then to re-fire the animation upon a user interaction, you will need to resort to the above technique with multiple images.</p>
<p>Multiple images to maintain, multiple HTTP requests, and an overall hacky, non-optimal solution to what could have been easily achieved with one SVG image.</p>
<ul>
<li>Embed the <strong>one</strong> SVG image on the page.</li>
<li>Create the animation any way you want/need. (Or create the animation before you embed the image.)</li>
<li>Play, pause, control the animation; and give the user the ability to control it as well.</li>
</ul>
<p>No extra HTTP requests for every image, no complicated animation timeline maintenance in graphics editors, and no accessibility concerns or woes that cannot be avoided with a few lines of code.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="average">GIF images cannot be stopped by the user without requiring extra images and extra HTTP requests. Even then, the control is not full.</td>
<td class="win">
SVG animations can be fully customized so that they are enabled, disabled and controlled by the user without requiring any hacky approaches.
</td>
</tr>
</table>
<h4 id="content-accessibility" tabindex="-1">Content Accessibility</h4>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="average">GIF images are only as accessible as PNG and JPEG images are—using an appropriate <code>alt</code> attribue value to describe them.
<p>The content inside the image cannot be discerned or made directly accessible to screen readers beyond what the overall image description does.</p></td>
<td class="win">
SVG images are accessible as well as semantic. The content inside the image that is being animated can also be described and made accessible to screen readers using SVG’s built-in accessibility elements, and enhanced using ARIA roles and attributes as well. (You can read all about making SVGs accessible <a href="http://www.sitepoint.com/tips-accessible-svg/">here</a>.)
</td>
</tr>
</table>
<h3 id="interactivity" tabindex="-1">Interactivity</h3>
<p>There’s not much to add here but the fact that you can interact with individual elements inside an SVG, during, before or after an animation, but that is not possible with a GIF. So, if you use a GIF, you will lose the ability to do anything beyond triggering or stopping the animation, and even those are not really done inside the SVG, as we’ve seen, but are achieved by swapping the GIF out with a static replacement. Even changing the colors of elements inside the GIF would require additional images to do so. That is yet another advantage to using SVG over GIFs.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="lose">Animations defined in GIF images cannot be interactive. You cannot interact with individual elements inside a GIF element, nor create links out of individual elements either.</td>
<td class="win">
SVG content is fully interactive. You can create hover and click interactions (and more) to which individual elements inside the SVG image can respond.
</td>
</tr>
</table>
<h3 id="responsive-%26-adaptive-animations" tabindex="-1">Responsive & Adaptive Animations</h3>
<p>The ability to animate SVG directly from the code, as well as manipulate its many, many attributes, results in and adds yet another advantage over GIF-based animations: the ability to create responsive, adaptive and performant animations, without adding any extra HTTP requests, using a few lines of code, and with quite smaller file sizes.</p>
<p>Sarah Drasner wrote <a href="http://www.smashingmagazine.com/2015/03/different-ways-to-use-svg-sprites-in-animation/">an article on Smashing Magazine</a> showing different ways to animate SVG sprites. One of these ways is having multiple “scenes” inside an SVG, animating them with CSS, and then changing the SVG “view”—by changing the value of <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">the <code>viewBox</code> attribute</a>—to show one scene at a time, depending on the current viewport size and available screen estate.</p>
<p>If you wanted to create the same animation using GIF images, you would lose the animation control capabilities as well as require multiple images which are probably going to be bigger (in file size) than the one SVG image.</p>
<p>But if you don’t want to go with animating SVG code, you could always create an SVG sprite and animate it the way you would animate any other image format—using <code>steps()</code> and a few lines of CSS. Sarah also talks about this technique in her article. Animating SVG images does not need to be complicated, and is generally performant.</p>
<p><strong>Conclusion</strong>:</p>
<table class="cagematch">
<tr class="th">
<td>GIF</td> <td>Animated SVG</td>
</tr>
<tr>
<td class="average">Given that content inside a GIF cannot be controlled with code, it is not possible to make the animations adapt or respond to viewport or context changes without resorting to seperate images.</td>
<td class="win">
Given that SVG content is directly animatable using code, the content as well as its animations can be modified so that they respond and/or adapt to different viewport sizes and contexts, without having to resort to any additional assets.
</td>
</tr>
</table>
<h3 id="final-words" tabindex="-1">Final Words</h3>
<p>GIFs have pretty good browser support, yes, but the advantages of SVGs outweigh theirs in almost every aspect. There might be exceptions, and in those cases do, by all means, use GIFs or any other image format that does a better job than SVG would. You might even use Video or HTML5 Canvas or whatever.</p>
<p>SVG can bring a lot of performance benefits to the table when compared to other image formats, especially GIFs.</p>
<p>Thus, given all of the above, I recommend that anywhere SVG could be used for animation, GIFs should be avoided. You’re free to ignore my recommendation, of course, but you’d be giving up on the many benefits that SVG animations offer.</p>
<p>Unless GIFs show a lot of advantages over SVGs that go beyond browser support for IE8 and below, then I believe SVGs should be the way to go.</p>
<p>A few resources to help you get started with SVG animations:</p>
<ul>
<li><a href="http://blogs.adobe.com/dreamweaver/2015/06/the-state-of-svg-animation.html">The State of SVG Animation</a></li>
<li><a href="http://www.smashingmagazine.com/2015/03/different-ways-to-use-svg-sprites-in-animation/">A Few Different Ways to Use SVG Sprites in Animation</a></li>
<li><a href="http://www.smashingmagazine.com/2015/09/creating-cel-animations-with-svg/">Creating Cel Animations with SVG</a></li>
<li><a href="http://greensock.com/">GreenSock</a> has a bunch of very useful articles on animating SVGs</li>
<li><a href="http://snapsvg.io/start/">Snap.svg</a>, also known as “The jQuery of SVG”</li>
<li><a href="https://davidwalsh.name/svg-animations-snap">SVG Animations Using CSS and Snap.SVG</a></li>
<li><a href="http://www.smashingmagazine.com/2014/11/styling-and-animating-svgs-with-css/">Styling and Animating SVGs with CSS</a></li>
<li><a href="https://jakearchibald.com/2013/animated-line-drawing-svg/">Animated Line Drawing in SVG</a></li>
</ul>
<hr class="line" />
I hope you found this article useful.
<p>Thank you for reading.</p>
<p class="note">Many thanks to Jake Archibald for reviewing and giving feedback to the article, and to Chris Lilley for his feedback re transparency in GIF images. It wouldn’t have been so comprehensive (read: ridiculously long) without their feedback. ^^</p>
Tips for Creating and Exporting Better SVGs for the Web
2015-11-16T00:00:00Z
https://sarasoueidan.com/blog/svg-tips-for-designers/
<p>Working with SVG in a RWD workflow usually involves a design phase and a development phase. The design phase is usually handled by designers who may or may not know how to code. And because of the nature of SVG as both an image format <em>and</em> a document format, every step taken in the graphics editor in the process of creating the SVG directly affects the resulting code and hence the work of the developer in charge of embedding, scripting or animating the SVG. In my day-to-day work, I am usually the developer whom designers hand the design assets over to, and SVG images are part of those assets.</p>
<p>Most of the assets I’ve been handed in my past projects needed a do-over and/or a second round of editing in the graphics editor before I could script them, because the resulting SVG code was not optimized enough for the kind of work—especially animation—that I was hired to do. The reason for that is that many of the designers I’ve worked with knew very little—if anything—about SVG <em>code</em>. They create vector graphics and UI assets all the time, but, for them, SVG is no more than an image format and they don’t know much about the code generated when their assets are exported as SVG documents.</p>
<p>There are some steps that designers can take or avoid—a set of “dos and don’ts”—that can help make the generated code cleaner. In this article, I want to share some of these. If you know any more, please do share them in the comments at the end of the article.</p>
<p>The tips we’re going to go over are applicable in Adobe Illustrator (Ai)—my graphics editor of choice—as well as other graphics editors. But since I personally use Ai, it is what I will be focusing on throughout this article.</p>
<p>We’re also going to go over the <strong>current</strong> SVG export options available in Ai and which ones to choose and why. But note that <strong>these options will change in the future</strong>, and this article will then be updated to reflect those changes.</p>
<p class="note">
That this article is based on my talk “SVG For Web Designers (and Developers)”—a talk I gave at CSSDevConf 2015 last month.
</p>
<p>So, let’s start.</p>
<p class="note update">
If you’re using Sketch to create SVGs, there are some things you can do to export cleaner code as well. Sean Kesterson has shared some tips in <a href="https://medium.com/sketch-app-sources/exploring-ways-to-export-clean-svg-icons-with-sketch-the-correct-way-752e73ec4694#.gbtebz7ex">this article</a>.
</p>
<h3 id="1" class="deeplink">1. Create Simple Shapes Using Simple Shape Elements, Not <code><path></path></code>s.</h3>
<p>There is a reason we have different basic shapes in SVG for creating, well, basic shapes. One could create practically any shape using a <code><path></code> element, right?</p>
<p>Simple shape elements (<code><line></code>, <code><circle></code>, <code><rect></code>, <code><ellipse></code>, <code><polygon></code> and <code><polyline></code>) are there for many reasons, and one of these reasons is that they are more readable and more maintainable and editable by hand than their <code><path></code> alternatives.</p>
<p>Basic shapes come with a set of attributes that allow you to control the shape features, such as position (<code>x</code>, <code>y</code>, <code>cx</code>, <code>cy</code>) and dimensions (<code>width</code> & <code>height</code>), while paths don’t come with these attributes.</p>
<p>For example, the following snippet shows the difference between a circle created and exported as a simple shape, versus one created and exported as a <code>path</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#FFFFFF<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28.1<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28.1<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>27.6<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- versus --></span></span><br /><span class="highlight-line"></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#FFFFFF<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M55.7,28.1<br /><span class="highlight-line"> c0,15.2-12.4,27.6-27.6,27.6</span><br /><span class="highlight-line"> S0.5,43.3,0.5,28.1</span><br /><span class="highlight-line"> S12.9,0.5,28.1,0.5</span><br /> S55.7,12.9,55.7,28.1z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></code></pre>
<p>If you want to animate your shape by, say, moving the position of the circle or making it bigger, you can do that by animating the position of the center via the x and y coordinates (<code>cx</code> & <code>cy</code>) and the radius of the circle (<code>r</code>). Whereas if you are working with a circle generated as a path, you will have to resort to CSS/SVG transformations (translation and scaling) to do that. And then suppose you want to animate that path and the animation requires you to apply further transformations to it? It can easily become a transformation mess.</p>
<p>Another advantage to using simple shapes is that, in the majority of cases, the code required to create a shape using simple shape elements is less than that required to create the same shape using a <code><path></code> element (see above snippet for a comparison), so using simple shapes will also result in a smaller file size, which is always better.</p>
<h3 id="2" class="deeplink"> 2. Convert Text to Outlines.. Or Don’t.</h3>
<p>To convert text to outlines:</p>
<ol>
<li>Select the text you want to convert.</li>
<li>Choose <strong>Type → Create Outlines</strong></li>
</ol>
<img src="https://sarasoueidan.com/assets/images/convert-to-outlines.png" alt="" />
<dl>
<dt>Pros:</dt>
<dd>
<ul>
<li>Text converted to outlines will preserve the font face used, without having to use a web font to display it. This means you save a few extra HTTP requests and don’t risk displaying your text with a fallback font that generally doesn't look good enough to substitute the beautiful font of your choice.
<p>
Outlining the text and preserving the font face used is good for preserving a brand’s identity when the latter is defined by the font face used, for example: in a logo. I almost always turn logo text to outlines. Outlining is also good for preserving the font face of certain scripts when used for headlines.
</p>
</li>
</ul>
</dd>
<dt>Cons:</dt>
<dd>
<ul>
<li>Text converted to outlines is not real text: it is a set of paths that form the outline (shape) of the text. Consequently, the text becomes unreal and inaccessible, not searchable and not selectable.
<p>
In the case of a script headline or even a logo where the text is outlined, using an <code>alt</code> text (if the logo is embedded as an image) or SVG’s <code><title></code> element is a good idea for providing alternative text for screen readers.
</p>
<p>I highly recommend reading all about making SVGs accessible in these two articles by Léonie Watson:</p>
<ul>
<li><a href="http://www.sitepoint.com/tips-accessible-svg/">Tips for Creating Accessible SVG</a></li>
<li><a href="https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/">Using ARIA to Enhance SVG Accessibility</a></li>
</ul></li>
<li>Converting text to outlines can significantly increase your SVG file size, depending on the complexity of the font face used. The image below shows the difference in the size (and readability) of an SVG with text converted to outlines (left) and text exported as SVG <code><text></text></code> (right).</li>
<figure>
<img src="https://sarasoueidan.com/assets/images/text-output.jpg" alt="" />
<figcaption></figcaption>
</figure>
<li>Paths are not easily controlled and animated as <code><text></code> elements (including <code><tspan></code>s) are. The latter have a set of attributes that give you more control over your animations, while path data is limited in that sense.</li>
</ul>
</dd>
</dl>
<h3 id="3" class="deeplink"> 3. Simplify Paths.</h3>
<p>A path is defined by a set of points which in turn are defined by a couple of coordinates each.</p>
<p>The less the number of points, the less the path data (<code>d</code> attribute), and, consequently, the less the overall SVG file size. This is always a good step to take because a smaller file size is better for performance.</p>
<p>To simplify a path:</p>
<ol>
<li>Select the path</li>
<li>Go to <strong>Object → Path → Simplify</strong></li>
<li>Tweak the number of points. Make sure you have the <strong>Preview</strong> checked so can see how the path changes as you change the number of points. Tweak the number to get to the minimum number of points while preserving (or sacrificing) as much of the path’s visual appearance as you need.</li>
</ol>
<img src="https://sarasoueidan.com/assets/images/simplify-paths.png" alt="" />
<img src="https://sarasoueidan.com/assets/images/path-points.png" alt="" />
<p>There is a video tutorial created by Adobe to explain the process; so if you are more into videos, you can check it out <a href="http://tv.adobe.com/watch/companion-videos-for-inspire/svg-for-the-web-using-the-simplify-panel-in-illustrator-cc/">here</a>.</p>
<p>You can also simplify paths using the <strong>Warp Tool</strong>. I’m not a designer and I usually use Ai’s Simplify algorithm to simplify my paths, so, if you’re a seasoned designer, you probably already know much more about the Warp tool than I do. There is <a href="http://www.smashingmagazine.com/2011/07/examples-and-tips-for-using-illustrator-s-warp-tools/">an article over at Smashing Magazine</a> all about this tool, in case you want to check it out.</p>
<h3 id="4" class="deeplink"> 4. Avoid Merging Paths Unless You Don’t Need Control Over Individual Paths.</h3>
<p>Many designers tend to combine or merge paths whenever possible. To merge paths:</p>
<ol>
<li>Select the paths you want to merge.</li>
<li>Go to <strong>Window → Pathfinder</strong></li>
<li>Click the <strong>Merge</strong> option among the list of options at the bottom of the panel (third icon from the left, shown in the screenshot below).</li>
</ol>
<figure>
<img src="https://sarasoueidan.com/assets/images/merge-paths.png" alt="" />
<figcaption></figcaption>
</figure>
<p>Combining paths may have its benefits, but avoid it when you or the developer needs to control and/or animate paths separately. Some animations are designed so that multiple elements are animated seperately, or sometimes you just want to style the paths using different fill colors. If you combine the paths, that will no longer be possible.</p>
<p>You need to make sure you know what the developer (or yourself, if you’ll be handling the development phase as well) needs and wants to do with the shapes you’re working on, and make the decision to merge or not to merge accordingly. This will save both of you a lot of time and friction.</p>
<h3 id="5" class="deeplink"> 5. Create Filters Using <em>SVG Filters</em>, Not <em>Photoshop Effects</em>.</h3>
<p>If you use the filters in the <strong>Photoshop Effects</strong> section under the <strong>Effect</strong> option, Illustrator is going to export the effects you create as raster images. For example, if you create a drop shadow using the <strong>Blur</strong> Photoshop effect, the drop shadow generated will be a raster image embedded inside the SVG either inline or externally, using <code><image></code>. You definitely don’t want that when you work with SVG.</p>
<p>To generate your effects as SVG code, you need to use the SVG Filters available:</p>
<ol>
<li>Go to <strong>Effect → SVG Filters</strong></li>
<li>Choose and use one of the filters available in that panel.</li>
</ol>
<img src="https://sarasoueidan.com/assets/images/svg-filters-in-ai.png" alt="" />
<h3 id="6" class="deeplink"> 6. Fit Artboard to Drawing.</h3>
<p>Have you ever embedded an SVG on a page, gave it a specific height and width and then found that it was being displayed at a size smaller than what you specified?</p>
<p>In <em>most</em> cases, this is caused by an amount of white space <em>inside</em> <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">the SVG viewport</a>. The viewport is displayed at the size you are specifying in your style sheet, but the extra space inside of it—around the graphic—causes your image to “shrink”, because that white space is taking an amount of, well, space, inside the viewport.</p>
<p>To avoid this, you need to make sure your artboard is <em>just big enough</em> to fit the drawing inside of it, but not any bigger.</p>
<p>The artboard dimensions are the dimensions of the exported SVG viewport, and any white space in the artboard will be generated as white space inside the viewport.</p>
<p>To fit your artboard to your drawing:</p>
<ol>
<li>Select the entire graphic. (I use <strong>cmd/ctrl + A</strong>.)</li>
<li>Go to <strong>Object → Artboards</strong> and choose the <strong>Fit to Artwork Bounds</strong> option.</li>
</ol>
<img src="https://sarasoueidan.com/assets/images/fit-artboard.png" alt="" />
<h3 id="7" class="deeplink"> 7. Use Good Naming, Grouping and Layering Conventions.</h3>
<p>I know this sounds like a no-brainer, but it needs to be emphasized for a few reasons:</p>
<ul>
<li><strong>The IDs and class names you use in the graphics editor are going to be translated to IDs and class names in the generated code.</strong> The more these names make sense and the clearer they label their respective elements, the less friction there will be when the developer works with the code.
<p>Now, I'm not saying you have to think up the perfect names—I'm sure we all have different ways of naming things and naming can be one of the hardest tasks, but labelling groups appropriately goes a long way. For example, if you are drawing a car, then using an ID of <code>wheel</code> to name the layer or group wrapping the shapes making up the wheel would be appropriate. If you are grouping all wheels in one group, you might give it an ID <code>wheels</code>. Simple names to tell elements and groups apart go a long way and save a lot of time, especially if the developer will be editing and manipulating the code by hand.</p>
<p>Illustrator does not do the best job at naming things, so specifying names helps reduce the amount of junk it produces. Granted, there will be some extra editing required to get rid of the annoying underscores that Ai insists on generating, but using proper names helps make this process a bit easier.</p>
<p class="note">As mentioned before, the next verison of Illustrator will show a big improvement in the way SVGs are generated, including generated IDs.</p></li>
<p><li><strong>Use layers to group related elements.</strong> Layers are translated into groups in code, so name these well too. Create layers/groups to wrap related elements together, especially those that might be animated as a whole. A lot of time could be spent reordering and grouping elements by hand by the developer if this is not already done in the design phase. To save time, make sure you group appropriately. Talking to the developer in the design phase and designing how the animation will be executed together is a big time saver.</li></p>
<p><li>If the images you’re creating are going to be used to create an SVG sprite, <strong>the names you use can and will be used by most automation tools to generate new files.</strong> So using clear and proper names will result in cleaner file names.
</li></p>
</ul>
<h3 id="8" class="deeplink"> 8. Choose The Best Suitable Export Options for the Web.</h3>
<p class="note update">
Starting with Illustrator CC 2015.2 released in November 2015, a new SVG Export workflow (File > Export > SVG) is available to export web-optimized SVG files for your web and screen design workflows. You can also choose to export individual objects versus the entire artboard. Refer to <a href="https://helpx.adobe.com/illustrator/how-to/export-svg.html">this article</a> for details.
</p>
<p>At the time of writing of this article, Illustrator comes with a bunch of export options that allow you to generate a generally better SVG code.</p>
<p>To export your SVG:</p>
<ol>
<li>Choose <strong>File → Save As</strong>
<img src="https://sarasoueidan.com/assets/images/file-save.png" alt="" /></li>
<li>Choose <strong>SVG</strong> from the dropdown menu
<img src="https://sarasoueidan.com/assets/images/save-as.png" alt="" /></li>
<li>Click <strong>Save</strong>.</li>
</ol>
<p>Once you click save, a dialog will show up that contains a set of options that you can customize, and that will affect the generated SVG code:</p>
<img src="https://sarasoueidan.com/assets/images/export-options.png" alt="" />
<p>The options shown in the image above are the ones recommended for generating SVG for the web.</p>
<p>Of course, you can choose to Outline text if you don’t want to use a web font; Illustrator provides you with an option to do it upon export, as you can see, as well.</p>
<p>The <strong>Image Location</strong> option specifies whether any raster images will be embedded inline in your SVG or will be external with a link inside the SVG. This, again, depends on what you need. Inlining images inside the SVG can increase its file size dramatically. Last time a designer sent me an SVG with an image inlined in it, the file size was more than 1MB! After removing that image (which was caused by the Photoshop Effects used, that we mentioned earlier), the file size dropped to less than 100KB! So, choose wisely.</p>
<p>The <strong>CSS Properties</strong> option gives you the option to choose how you want the styles inside the SVG to be created: using presentation attributes, inline styles, or in a <code><style></code> tag. This is also a matter of preference and depends on how you intend to manipulate the SVG once you’ve embedded it. If you’re not the one who’s going to do that, make sure you consult with the developer to choose the option that suits their needs best.</p>
<p>The less the number of <strong>Decimal Places</strong>, the less the file size of the SVG. One decimal place should generally be enough, so I’d go with that.</p>
<p>Note that if you choose 3 or 4 decimal places, for example, and then use an optimization tool to optimize the SVG and bring that number down back to 1, the SVG might end up visually broken; so it is best to choose this option early on.</p>
<p>There is more to the options panel than what I have covered. Adobe’s Michaël Chaize has written an excellent article about the export panel that explains what each option does exactly. I highly recommend checking his article out:</p>
<ul>
<li><a href="http://creativedroplets.com/export-svg-for-the-web-with-illustrator-cc/">Export SVG for the web with Illustrator CC</a></li>
</ul>
<p>Now, at the time of writing of this article, Illustrator will still generate unnecessary code such as editor metadata, empty groups, among others, so you will need to optimize the SVG further after you’ve exported it, be it by hand, or using a standalone SVG optimization tool.</p>
<p>But before we jump into the Optimization section, I want to note that you may or may not want to check an extra option as you save the SVG: the “<strong>Use Artboards</strong>” option, in the Save panel:</p>
<img src="https://sarasoueidan.com/assets/images/use-artboards.png" alt="" />
<p>This option is useful for when you are working with multiple SVG images (for example: icons) and you are using an artboard for every icon.</p>
<p>Exporting multiple artboards will generate multiple <code>.svg</code> files, one for each artboard (one for each icon).</p>
<p>If you are working with only one artboard, this option will be disabled by default.</p>
<p>Choosing to export one or multiple SVG files depends on how the SVG is going to be embedded.</p>
<p>For example, if you are going to create an SVG sprite for an SVG icon system, there are several ways you can create and use the sprite, and each one would require a different approach: one technique requires the icons to be separate at first, and another requires them to be part of one image.</p>
<p>I will be writing a separate post about spriting SVGs and the artboard options, but until then, you can get an overview of the different spriting techniques in the following article I wrote for <a href="http://24ways.org/">24Ways.org</a>:</p>
<ul>
<li><a href="https://24ways.org/2014/an-overview-of-svg-sprite-creation-techniques/">An Overview of SVG Sprite Creation Techniques</a></li>
</ul>
<h4 id="optimize" class="deeplink"> To Optimize or Not to Optimize...</h4>
<p>It is usually recommended to optimize the SVG after exporting it from a graphics editor using a standalone optimization tool. The current most popular optimization tool is the NodeJS-based tool called SVGO. But it may not always be a good idea to optimize your SVG, especially if you intend to animate it.</p>
<p>If you intend to script and/or animate the SVG, you’re likely to set up a certain document structure—wrapper groups, ID names that you are not using/referencing inside the SVG but intend to use in your JavaScript, etc. This structure is going to change if you optimize your SVG using SVGO (or any other optimization tool).</p>
<p>Optimization tools usually remove any unused groupd and IDs, as well as apply many changes to the SVG to make sure it is optimized well.</p>
<p>I’ve once optimized an SVG after applying an animation to it using <code><animate></code>. The SVG was broken and so was the animation inside of it, because the entire structure was changed. So that is something to keep in mind before optimizing SVGs.</p>
<p>If you’ve manually edited and/or generated an SVG with a certain structure that you need, avoid optimizing using an optimization tool, and optimize by hand as much as possible. Some editor junk at the beginning and end of the SVG can be easily removed by hand. Other junk, such as metadata and classes generated by editors like Sketch—which has no SVG optmization options, can be harder to optimize by hand.</p>
<p>I generally never use Sketch to generate complex SVGs. I use Illustrator or Inkscape; the latter comes with a default export panel which gives you a lot of options to optimize your SVG before exporting it (see image below). Inkscape generates the cleanest SVG code at the time of writing of this article—that is, if you choose the <strong>Optimized SVG</strong> option, but the blurriness of the UI on a retina screen as well as its dependency on X11 on OS X make it a pain to use, so I am currently sticking with Illustrator.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/inkscape-export.png" alt="" />
<figcaption></figcaption>
</figure>
<p>If you do need/want to optimize your SVG, SVGO is the tool I would recommend.</p>
<p>SVGO comes with a bunch of plugins that you can fit into practically any kind of workflow. You can find more information about those tools in the following article I wrote a few months ago:</p>
<ul>
<li><a href="https://sarasoueidan.com/blog/svgo-tools">Useful SVGO[ptimization] Tools</a></li>
</ul>
<h3 id="9" class="deeplink"> 9. Communicate. Communicate early.</h3>
<p>Possibly the most important tip I can give is to communicate, and to do it early in the design process.</p>
<p>I’m now assuming that you—the designer creating the SVG—are not the same person responsible for developing the SVG (scripting, animating, embedding, etc.).</p>
<p>Almost every one of the above tips requires knowledge of the development phase and what the developer intends to do with the SVG—how they intend to embed, script, style and animate it. So, unless you’re the same person making decisions for both phases, and unless you want to waste a lot of time reiterating and editing the SVGs, you need to make sure you know what the developer needs to do with the SVG and what approach(es) they will be taking. If you’re working on a project that has a tight deadline, you probably can’t afford to waste a big amount of time making changes and revisions to image assets, when you can avoid that by communicating early.</p>
<p>Designers and developers can be each other’s best friends. The very nature of SVG requires both design and development phases to be open to one another, and this, in turn, requires the designer(s) and developer(s) to talk, <em>before</em> the design process begins, and throughout the process as well.</p>
<hr />
<p>Thank you for reading.</p>
Developer of the Year 2015 net Award
2015-09-22T00:00:00Z
https://sarasoueidan.com/blog/developer-of-the-year-2-15-net-award/
<p class="deck">
Last Friday, I gave my first keynote in the UK at Generate Conf. That same day, the <a href="http://thenetawards.com/"><em>net</em> awards</a> were announced. On Saturday, I came back home with an award: the <strong>Developer of the Year</strong> award. This is a short thank you post to everyone involved.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/netaward-me.png" alt="Developer of the Year net award" />
<figcaption>
Photo by <em>net</em> magazine. All rights reserved. (<a href="https://www.flickr.com/photos/netmag/21647815869/in/faves-136619150@N04/">Original Photo</a>)
</figcaption>
</figure>
<p>A few months ago, many fellow designers and developers nominated me for two <em>net</em> awards: Outstanding Contribution and Developer of the Year.</p>
<p>Last Friday, the winners of the different categories were announced, and I’m thrilled to have won the Developer of the Year award.</p>
<p>I can’t even begin to explain how happy and humbled that makes me. To be recognized and appreciated by your fellow colleagues is a great privilege, and a badge I wear with pride.</p>
<p>I haven’t gotten a chance to say thanks to <a href="https://storify.com/SaraSoueidan/netawards-2015-developer-of-the-year">each and every one who congratulated me over Twitter</a> after the award announcements—the feedback was so overwhelming (and fast!) that I just couldn’t keep up, so I thought I’d wait till I’m back home to write a thank you post instead.</p>
<p>So: <strong>thank you</strong>! Thank you to every one who nominated me and deemed me worthy of this title, and thank you to every one who voted and supported me. Two and a half years ago, when I started working in this field, I didn’t expect to be here today. I had different plans, most of which were unclear, and I never thought when I wrote <a href="http://sarasoueidan.com/blog/windows8-animations">my first blog post</a> (on a really ugly blog design :P) that I’d end up shifting my overall path and doing all the writing and speaking that I am doing today. I am thankful to God and grateful for each and every step and opportunity I got along the way, and for all your wonderful support.</p>
<p>And now, since I’m really bad at coming up with proper ‘speeches’, I’ll keep it short and end with a huge Thank You one more time. You are awesome! ^_^</p>
<p>–S</p>
CSS vs. SVG: The Final Round(up)
2015-09-16T00:00:00Z
https://sarasoueidan.com/blog/css-vs-svg-the-final-roundup/
<p class="deck">
This is the last article in the series of article comparing common CSS techniques to their SVG counterparts. This article is a roundup of several CSS and SVG solutions, as opposed to being an article comparing one solution that can be achieved using either CSS and SVG. There are already a bunch of excellent articles out there that cover the details for each of these solutions, so we will get an overview of each solution and link to those articles for each section as we go. I highly recommend you check them all out.
</p>
CSS vs. SVG: Shapes and Arbitrarily-Shaped UI Components
2015-09-01T00:00:00Z
https://sarasoueidan.com/blog/css-vs-svg-arbitrarily-shaped-ui-components/
<p class="deck">
This post is the third in the series of posts exploring techniques and examples that can be achieved using both CSS and SVG, and compares them both.In this article, we are going to go over techniques for creating arbitrarily-shaped UI components using CSS properties and SVG’s capabilities, and a mix of both! Specifically, we will be talking about how to create circular menus, as these are the perfect example of usable non-rectangular UI elements.
</p>
CSS vs SVG: Styling Checkboxes and Radio Buttons
2015-08-26T00:00:00Z
https://sarasoueidan.com/blog/css-vs-svg-styling-checkboxes-and-radio-buttons/
<p class="deck">
This post is the second in the series of posts exploring techniques and examples that can be achieved using both CSS and SVG, and compares them both.
In this article, we’re going to look into ways to style checkboxes and radio buttons using both CSS and SVG. You will learn how you can create animated checkboxes using SVG's line drawing capabilities.
</p>
Building A Circular Navigation with CSS Clip Paths
2015-08-17T00:00:00Z
https://sarasoueidan.com/blog/building-a-circular-navigation-with-css-clip-paths/
<p class="deck">
The CSS <code>clip-path</code> property is one of the most underused and yet most interesting properties in CSS. It can be used in conjunction with CSS Shapes to create interesting layouts, and can be taken to the extreme to create some incredibly impressive layouts and animations like the Species in Pieces project. While exploring the creation of arbitrarily-shaped UI components using CSS and SVG, it occurred to me that the clip-path property, when combined with SVG paths, can be used to create circular menus fairly easily, especially considering the (expected) browser behaviour when handling pointer events on clipped regions as per the specification. In this article, we'll explore this idea further and learn how it's done.
</p>
CSS vs. SVG: Graphical Text Effects
2015-07-31T00:00:00Z
https://sarasoueidan.com/blog/css-vs-svg-graphical-text/
<p class="deck">
This post is the first in a series of posts exploring techniques and examples that can be achieved using both CSS and SVG, and compares them both. Since I am biased to SVG, this series is really intended to prove that SVG — because of its nature as both an image and a document format — is simply better than CSS when it comes to solving certain design problems on the web. But to keep an objective point of view, we will be weighing the pros and cons of each technique and find out where and when CSS or SVG can serve as a better tool to the design goals at hand.
In this article, we’re going to look into the CSS <code>background-clip</code> and <code>mask-image</code> properties, as well as SVG pattern fills and masks.
</p>
Chapter 4, Smashing Book 5
2015-07-21T00:00:00Z
https://sarasoueidan.com/blog/smashing-book-5/
<p class="deck">
The new Smashing Book is out! It’s packed with a <em>lot</em> of time-saving, practical techniques for crafting fast, maintainable and scalable responsive websites. I wrote a chapter in the book—<strong>Chapter 4: Mastering SVG For Responsive Web Design</strong>. Here is an overview of what that chapter covers, and why I think you should buy the book.
</p>
<figure style="position: static; width: 100%;">
<img src="https://sarasoueidan.com/assets/images/smashing-book-5-1.jpg" alt="Image of the Smashing Book 5." />
<figcaption>Photo credit: <a href="https://twitter.com/MatCompagnucci/status/622441177795723264/photo/1">Mattia Compagnucci</a></figcaption>
</figure>
<p>I always get questions from my followers about a good place to start learning SVG. I <a href="https://sarasoueidan.com/tags/svg/index.html">write about SVG a lot</a>, but I realize that it’s always better to have one place where you can jump start whatever you’re learning and then take it from there. I believe the SVG chapter in the book is a great place to do just that.</p>
<p>Chapter four of the Smashing Book 5 is <strong>80 pages</strong> of SVG, covering everything you need to know to start implementing SVG in your responsive web designs today.</p>
<br />
<blockquote class="testimonial testimonial--right grey">
There is a chapter on SVG for RWD by Sara Soueidan that kind of made me feel stupid, not because of how it was written, that was great, but because it introduced me to so many new things about SVG. What the heck have I been learning all of these years?
<cite>—<a href="https://www.makerscabin.com/mag/smashing-book-5-a-review/">Paul Scrivens, “Smashing Book #5, A Review”</a></cite>
</blockquote>
<h3 class="deeplink" id="chapter-contents">So, What Can You Expect To Learn About?</h3>
<figure style="position: static; width: 100%;">
<img src="https://sarasoueidan.com/assets/images/smashing-book-5-2.jpg" alt="Image of the Smashing Book 5, chapter 4." />
<figcaption>Photo credit (and thanks to): <a href="https://twitter.com/MatCompagnucci">Mattia Compagnucci</a></figcaption>
</figure>
<p>The answer to this question in my head is always a list of what is <em>not</em> covered in the chapter, because the topics covered include so much! Here is an overview of the contents:</p>
<ul>
<li><strong>What is SVG?</strong>: a brief overview of the image format and its history.</li>
<li><strong>Vector vs. Raster</strong>: a comparison between vector and raster image formats such as JPEG, PNG, etc., characteristics of each, and what makes the SVG format stand out.
<ul>
<li><strong>Advantages of SVG</strong>: the many, many features that makes SVG a better image format for responsive web design, including but not exclusive to: scalability and resolution-independence, performance, accessibility, animation, and more.</li>
<li><strong>Vector vs Raster: Which is the Better Format?</strong>: a set of guideliness and things to consider when choosing the image format for your graphic, including why and when SVG might or might not be a good choice.</li>
</ul>
</li>
<li><strong>Quick Overview of SVG Syntax and Code</strong> including…
<ul>
<li><strong>Understanding the SVG Viewport and viewBox</strong></li>
</ul>
</li>
<li><strong>Creating and Exporting SVGs in Vector Authoring Tools</strong>: tips, dos and don’ts for the right workflow when designing SVG images that ensure the resulting code is as optimal as possible. This section includes:
<ul>
<li><strong>Exporting SVG for the Web From Adobe Illustrator</strong>: overview of the export options and which ones to pick to make sure you end up with better code.</li>
</ul>
</li>
<li><strong>Optimizing SVGs Using Standalone Optimization Tools</strong>: there are a lot of tools, each can fit into a different workflow. We go over a list of some of the best tools.</li>
<li><strong>Embedding SVGs</strong>: the different ways to embed SVGs on a page, and the pros and cons of each, tips on improving performance when choosing some of them.</li>
<li><strong>Using SVGs as an Icon Font Replacement
(or, How to Create SVG Sprites and Use Them as an Icon System)</strong>: what the tite says. Sections covered in this master section include:
<ul>
<li><strong>SVGs vs Icon Fonts</strong>: SVGs are a far more superior icon system, and this section covers all the reasons why, as in intriduction to…</li>
<li><strong>Spriting SVGs</strong>: introduction to the concept, followed up:
<ul>
<li><strong>SVG icon system with icons as background images</strong>: the how-to of creating an SVG sprite for icons used as background images on the page, including tools to simplify the process.
<ul>
<li>Covered is also a section about SVG data URIs, including any performance considerations.</li>
</ul>
</li>
<li><strong>SVG Icon System with Icons as Foreground Images</strong>: how-to and workflow automation tools.
<ul>
<li><strong>Styling the icons and applying transitions to them</strong>: including styling the contents of <code><use></code>.</li>
</ul>
</li>
</ul>
</li>
<li><strong>Providing and Automating Fallback for HTML-Inline SVGs</strong>: some known and some lesser known techniques for:
<ul>
<li><strong>SVG fallback as foreground images</strong>: how to provide fallback for SVG images that are embedded as foreground images. There are several options, all covered.</li>
<li><strong>SVG fallback as background images</strong>: also a few options to choose from leveraging different CSS technqiues.</li>
<li><strong>SVG Fallback Using SVG Conditional Processing (SVG Fallback as Background Image)</strong>: SVG’s built-in fallback mechanism.</li>
<li><strong>Other Fallback Techniques</strong> for different embedding techniques.</li>
</ul>
</li>
<li><strong>Better SVG Spriting Using Native Fragment Identifiers</strong>: this is my personally-favourite SVG spriting tchnique and one of the lesser used ones, and the one closest to the way PNG image spriting works.</li>
</ul>
</li>
<li><strong>Making SVG’s Cross-Browser Responsive with CSS</strong> including:
<ul>
<li><strong>Making SVGs Fluid with CSS</strong>: everything including browser bugs and workarounds.</li>
<li><strong>Making SVGs Adaptive with CSS</strong> using CSS Media Queries to show and hide (or even animate) parts of the SVG depending on viewport size.</li>
</ul>
</li>
<li><strong>Making SVGs Accessible</strong>: the least you can do to provide even the most basic accessibility to your SVG images today.</li>
<li><strong>Using SVGS to Optimize the Delivery of Other Image Formats</strong>: use cases that go beyond simply displaying or animating vector images. It includes:
<ul>
<li><strong>Using SVG Masks to Optimize Raster Graphics</strong></li>
<li><strong>Using SVG As a Container for Serving Responsive Images</strong> a.k.a the Clown Car Technique.</li>
</ul>
</li>
<li><strong>Where To Go From Here</strong>: pointers to more ideas and resources to look into.</li>
</ul>
<p>As you can see the chapter covers <em>a lot</em>. The topics it does not cover (otherwise it would have turned into a book) are: graphical effects (such as filters) and animation. I’m pretty sure there is even more about SVG to get into that the chapter does not cover, but hey, it’s only a chapter of a book!</p>
<br />
<blockquote class="testimonial testimonial--right grey">
Reading the new Smashing book, which is timely as I'm spritifying some SVGs, and it's easier than flying @SaraSoueidan over to do it for me.
<cite>—<a href="https://twitter.com/brucel/status/623169965189918720">Bruce Lawson</a></cite>
</blockquote>
<h3 class="deeplink" id="the-reviewers">The Reviewers</h3>
<p>The chapter was reviewed by <a href="http://twitter.com/DmitryBaranovsk">Dmitry Baranovskiy</a> (creator of the <a href="http://raphaeljs.com/">Raphael</a> library and its modern <a href="http://snapsvg.io/">Snap.svg</a> alternative) and <a href="http://twitter.com/jaffathecake">Jake Archibald</a> (Google), both experienced SVG developers.</p>
<h3 class="deeplink" id="designers-complete-reference">A Designer’s Complete Reference</h3>
<p>The chapters of the book are complimentary to each other, so the entire book is a collection of topics that go extremely well together, giving you a set of diverse topics that are sure to come in handy for any responsive design project you work on.</p>
<p>While writing the SVG chapter, I mentioned the problems that non-vector images might bring up, and how the new <code><picture></code> element emerged as the almost-perfect solution for that problem. (SVG still has its benefits over these formats, of course, but raster images still have their place and importance where SVGs just can’t fill in.) And since my chapter was focused on SVG, I couldn’t get into the details of how to serve responsive raster images. Yoav Weiss’ chapter fills in that spot and covers everything you need to know about using responsive images. Not only that, but Yoav’s chapter also includes tips that you can apply to SVG images as well, making these two chapters the ultimate reference for images used in responsive design.</p>
<p>You can’t expect an SVG chapter to not mention SVG fonts, too. But then again, since that topic is outside the scope of what could be covered in the chapter, Bram Stein’s chapter on web font performance fills in that spot in his chapter that covers everything you need to know about web font formats, performance, fallback, and more.</p>
<p>The book was written by some of the brightest people in our industry, all experts in their fields, smarter than me, and known for their expertise in specific topics—all of which they covered in lengthy, very informative articles. You will find topics ranging from design workflows, patterns, to content choreography, images, advanced layout with Flexbox, responsive process, performance optimization, all the way to optimizing for offline experiences.</p>
<p>I hope you find the chapter on SVG useful, and I’m confident you will find the remaining chapters insightful as well. I promise you will not regret it.</p>
<p>So, what are you waiting for? <a href="http://www.smashingmagazine.com/2015/03/31/real-life-responsive-web-design-smashing-book-5/">Go grab your copy</a> and feel free to <a href="http://twitter.com/SaraSoueidan">tweet at us</a> with any feedback or comments (or even pretty pictures)!</p>
<div class="button-wrapper"><a href="http://www.smashingmagazine.com/2015/03/31/real-life-responsive-web-design-smashing-book-5/" class="button ghost">Buy the Book</a></div>
Styling the Contents of SVG<use> with CSS
2015-07-16T00:00:00Z
https://sarasoueidan.com/blog/styling-svg-use-with-css/
<p class="deck">
An in-depth article on how to style the contents of the SVG <use> element and overcome some challenges it brings. We get into where the contents are cloned (the shadow DOM!), what limitations that brings up and how to work around them by taking advantage of the CSS cascade and using CSS Variables to get full control over the content while providing fallback for non-supporting browsers.
</p>
Art-Directing an SVG Embedded Using <object>
2015-07-08T00:00:00Z
https://sarasoueidan.com/blog/art-directing-svg-object/
<p class="deck">I have recently finished a front-end development project for <em>Provata Health</em> — a US-based health and wellness company specializing in health promotion and behavior change science. As part of their <a href="http://provatahealth.com/">marketing website</a>, I worked on an infographic that showcases the three major health results one can achieve by following their health program. The graphic is a perfect candidate for SVG and you’ll see why throughout this article. But even though almost all of the graphics on the site were vector (which made the entire project exciting to me), I want to focus on this particular graphic and how and why I made the development choices I made while embedding it.</p>
<p>I had to make a few “unusual” decisions to make sure I provide the best performance and accessibility possible, including inlining a piece of JavaScript in the middle of the page. So I wanted to share why I made those decisions, and at the same time shed the light on yet another case where SVG could be improved to adapt to our development needs.</p>
<p>Also, after <a href="https://twitter.com/SaraSoueidan/status/611163768811323392">tweeting</a> about inlining the JavaScript in the middle of the document, I asked my go-to performance specialist—the nice Mr. <a href="http://twitter.com/aerotwist">Paul Lewis</a>—about any performance implications, he too thought I should write about this and share why I made that decision. So here I am.</p>
<p>First, let’s have a look at that graphic and see why it is a perfect SVG candidate, before we get into embedding, art-directing and fallback.</p>
<h3 class="deeplink" id="theresults-svg-graphic">The Results SVG Graphic</h3>
<p class="note">
Disclaimer: I am writing this case study and sharing this image after having gotten a written approval from my client to do so. You can check the graphic out live <a href="http://provatahealth.com/results.html">here</a>.
</p>
<p>This is the SVG graphic I worked on in the website’s <em>Results</em> page, and how it was supposed to look on mobile and desktop, respectively.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/provata-results-graphic.jpg" alt="The Provata Results Graphic as displayed on mobile (left) and desktop (right)." />
<figcaption>The Provata Results Graphic as displayed on mobile (left) and desktop (right). You can check the graphic out live <a href="http://provatahealth.com/results.html">here</a>.</figcaption>
</figure>
<p>The text inside of the graphic is just as important as the rest of the HTML text on the page, with the addition of the positioning that illustrates what category each result belongs to in a nice visual manner. When you have text like this, SVG should be the first thing to think about. This kind of graphic (similar to an infographic) is a perfect candidate for SVG and one that makes SVG’s accessibility and visual features shine.</p>
<p>Imagine having to absolute-position each and every piece of text and image inside that graphic in an HTML document, taking into account and document flow (or lack thereof); doing this using SVG not only makes more sense but also comes with the advantage of being able to use a graphics editor to achieve all of the positioning visually, instead of hand-coding the positions using pixels or <code>em</code>s (or whatever unit you prefer).</p>
<p>Not only that, but you also get the fluidity and responsiveness of SVG images and text by default, so you won’t have to worry about adapting any text and image sizes to different viewport sizes. Not to mention text accessibility inside of the image. Win win win!</p>
<h3 class="deeplink" id="choosing-the-embedding-technique">Choosing The Embedding Technique</h3>
<p>Since the contents of the SVG are not animated (they were at first, but the UX team decided to drop the animations later for better UX), then the first logical embedding technique that comes to my mind is <code><img></code>, or its cool cousin <code><picture></code>; either way, it would be embedded as-an-image—as a foreground image, to be specific, because 1) there is no reason to embed it as a background image and 2) because <a href="http://www.stevesouders.com/blog/2015/05/12/hero-image-custom-metrics/">foreground images have better performance metrics than background images</a>.</p>
<p>However, there is one important reason why embedding it as-an-image is not an option: not only does the text inside of the image need to be as “real” (read: readable, selectable and searchable) as any other piece of text on the page, but <strong>we also need to be able to provide this same text as a raw text fallback</strong> in case the SVG fails to load for any reason.</p>
<p>So, to recap:</p>
<ol>
<li>The text inside of the image needs to be accessible to screen readers.</li>
<li>The text inside of the image needs to be selectable and searchable by the reader.</li>
<li>The text inside of the image <strong>should be the fallback provided for non-supporting browsers</strong>. That is, if the image fails to load for any reason, the text content of the image should be the content to replace it, <em>not</em> a PNG or JPEG version of the graphic. This decision was one of high importance to me because the entire section of that page depended on that text to convey a message, so should the user not be able to read the SVG text, I wanted them to be able to simply <em>read</em> the text content.</li>
</ol>
<p>Given all of the above, <code><object></code> was the perfect candidate and the element I eventually used to embed the graphic.</p>
<p>To make sure the text inside the SVG is accessible, searchable and selectable, make sure you don’t turn it into outlines. Keep it as default SVG <code><text></code>. With this, you also get the ability to apply the page’s font face to the SVG text as well, using <code>@font-face</code>. So you get the text rendered and working just like you expect it to.</p>
<p>Between the opening and closing <code>object</code> tags, instead of providing a PNG version of the image for non-supporting browsers—which is what most developers would normally opt for, I provided a raw text alternative to the graphic, and the content of that text was the exact same as the content inside of the SVG graphic.</p>
<p>This works pretty well. While testing the graphic on mobile, and before finalizing the JavaScript (see next section), the graphic failed to load in one of the tests because the URL of the image I had specified was incorrect, so I got the text content in that section of the page instead, and it looked and worked perfect for the purpose of that section, as opposed to having loaded a PNG version that could have been unreadable.</p>
<p>Having chosen the embedding element, decided for an accessible fallback, and knowing that we need to provide two seperate images for desktop and mobile, it’s time to handle the image swapping…</p>
<h3 class="deeplink" id="art-direction">Art Direction</h3>
<p>I’m a huge proponent of the <code>viewBox</code> clipping technique. The attribute is extremely powerful and art-directing an image using this attribute is always the first thing that comes to my mind. If you’re not familiar with how this works, you should read my <a href="https://sarasoueidan.com/blog/svg-art-direction-using-viewbox/">previous blog post</a> explaining exactly how that’s done.</p>
<p>However, this is one use case where the <code>viewBox</code> clipping technique would not suffice because clipping would not be enough to hide the parts we do not need on smaller screens, because the <code>viewBox</code> can only clip to rectangular areas, so we would either have to use a custom clip path to clip out the excess content, or we could hide them by making them invisible (using <code>opacity</code>, <code>visibility</code>, <code>display</code>, etc.), and the latter can be <a href="http://tympanus.net/codrops/2014/08/19/making-svgs-responsive-with-css/">easily achieved using CSS media queries</a>. So the way it would work would be:</p>
<ol>
<li>Hide the branched parts (text and small illustrations) on small screens using <code>opacity: 0;</code> or <code>display: none</code>, for example</li>
<li>Then clip the canvas to the remaining graphic (the three overlapping colored circles) to get rid of unwanted excess white space resulting from hiding the rest of the graphic.</li>
</ol>
<p>Changing the viewBox using CSS is currently not possible, so that would require JavaScript. Hiding the unwanted parts on small screens is possible in CSS, though. So this makes it possible to do half of the job using JS and the other half using CSS. I don’t like the sound of this, but this is the only way it’s possible now. Ideally, we would be able to change the <code>viewBox</code> using nothing but CSS, thus croppig and hiding the content using a few lines of CSS, but that’s currently not possible. (See previous article for details.)</p>
<blockquote class="pull-quote">
art-directing SVG using the `viewBox` attribute is a powerful technique but one you should not pursue if the graphic you are embedding is too big to serve in full composition on mobile.
</blockquote>
<p>So, we can use one SVG image and art-direct it using CSS and a few lines of JavaScript. Great.</p>
<p>However, something else must be kept in mind here: <strong>performance</strong>. In my previous article on this subject, I mentioned that art-directing SVG using the <code>viewBox</code> attribute is a powerful technique but one you should not pursue if the graphic you are embedding is too big to serve in full composition on mobile. And in the case of the Provata graphic, the hidden parts were indeed non-trivial in contributing to the overall file size, making it significantly bigger, so using the same full image and hiding parts of it on mobile was definitely not a suitable approach in this case.</p>
<p>Which brings us to the next section…</p>
<h3 class="deeplink" id="art-directing-the-svg-object">Art-Directing The SVG <code>object</code></h3>
<p>Instead of serving the same graphic for desktop and mobile, we opted for two different graphics. Both of them were embedded using an <code><object></code> tag.</p>
<p>Had we embedded the SVG as an image, we would have been able to easily switch the source of the image using the <code><picture></code> element and its <code><source></code> descendant. I wrote all about this in a previous article as well, so you might want to <a href="https://sarasoueidan.com/blog/svg-picture">check it out</a>. The code for that would look something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 640px)<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>results-graphic--small.svg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>results-graphic--full.svg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>results-graphic--default-fallback.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Header description..<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>But since we need the text fallback and are using <code><object></code>, we need to swap out the source of the <code>object</code> using JavaScript because it’s not possible to do so using markup…</p>
<h3 class="deeplink" id="using-js-to-switch-object-source">Using JavaScript to Switch <code>object</code> Source</h3>
<p>To detect viewport size, I like using <a href="http://modernizr.com/">Modernizr</a>. So the function swapping the <code>object</code> source looks something like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">// after getting a reference to the graphic...</span></span><br /><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">changeSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">var</span> url <span class="token operator">=</span> graphic<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'data'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>Modernizr<span class="token punctuation">.</span><span class="token function">mq</span><span class="token punctuation">(</span><span class="token string">'(max-width: 767px)'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span>url <span class="token operator">==</span> <span class="token string">"path/to/results-graphic--mobile.svg"</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> graphic<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'data'</span><span class="token punctuation">,</span> <span class="token string">'path/to/results-graphic--mobile.svg'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// maybe show/hide something else related to it here</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span>url <span class="token operator">==</span> <span class="token string">"path/to/results-graphic--desktop.svg"</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> graphic<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'data'</span><span class="token punctuation">,</span> <span class="token string">'path/to/results-graphic--desktop.svg'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// maybe show/hide something else related to it here</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Of course, make you sure sure you trigger this function and re-trigger on window resize, and since the function already checks for whether or not the source is the one required at a specific screen size, it won’t keep swapping the source every time (that would otherwise cause a horrible flash making the image unusable). The rest is self-explanatory.</p>
<h3 class="deeplink" id="inline-or-external-js">Inline or External JS? Sync or Async?</h3>
<p>I inlined the script right after the <code></object></code> tag in the markup, and did <em>not</em> make it <code>async</code>.</p>
<p>“<em>Why?</em> 😳”</p>
<p>I needed the browser to parse the JS and specify the <code>object</code> source <strong>as soon as possible</strong>, I did <em>not</em> want it to parse the entire document before it displayed the graphic, because it’s the most important graphic on the page and I wanted it to show up as soon as possible. So, I did not async the inline script.</p>
<p>(We devs can turn any word into a verb and it’s totally legit.)</p>
<p>(Yes it is.)</p>
<p>At first, I was relying on the JS to set the <code>object</code> source on both mobile and desktop. That caused some issues on mobile—loading- and performance-wise, so I ended up setting the <code>data</code> to point to the mobile version of the graphic by default, and have the script do the swapping to the desktop version when on desktop. That got rid the mobile loading issue, and, expectedly, works pretty well on desktop.</p>
<p>Now, this works, perf is pretty good on both desktop on mobile <em>and</em> I even got a “<em>looks good to me</em>” from Paul, so I took that as my “good to go” sign.</p>
<p>The takeaway here is: <strong>always test your pages</strong>, no matter what technique you use to achieve something. It turned out that inining small pieces of JavaScript is sometimes even recommended to improve load times, <a href="https://developers.google.com/speed/docs/insights/BlockingJS#InlineJS">says Google</a>, and that’s exactly what I achieved by inlining the JS in this case.</p>
<h3 class="deeplink" id="an-object-extension">An <code>object</code> Extension?</h3>
<p>As I worked on this project, I <a href="https://twitter.com/SaraSoueidan/status/611142858221973504">tweeted</a> about how nice it would be if we have a <code><picture></code> cousin that did the same thing as <code><picture></code> does for <code><img></code>, but something more <code>object</code>-oriented. (Pun not intended.)</p>
<p>Art-directing an SVG embedded as an <code><object></code> is something I’ve done more than once, and all in real-life, practical use cases as part of client projects. So this is something that is useful and would make a lot more sense if we could do it <strong>using markup</strong> <em>without</em> having to resort to (inline) JavaScript.</p>
<p>After second thoughts, I think that maybe, instead of coming up with a new element to do this, <em>extending</em> the <code>object</code> element with a <code><source></code> or <code><data></code> element that is similar to <code>source</code> might be quite handy.</p>
<p>So we could do something like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>object</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>data</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span> <span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span> <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>data</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span> <span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span> <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>object</span><span class="token punctuation">></span></span></span></code></pre>
<p>This is just an idea, and probably not even close to something implementable—it is just meant as an idea that might be shaped into something usable/implementable or maybe a similar idea that achieves the same functonality could spawn from it.</p>
<p>This is yet another thing added to my SVG wishlist. Until we get anything like that, JavaScript is the way to go to swap <code>object</code> sources.</p>
<p class="note">
<em>UPDATE: After reading the article, <a href="http://twitter.com/tigt_">Taylor Hunt</a> pointed me to <a href="http://www.w3.org/TR/SVGParamPrimer/">this specification</a>, which defines a <code><param /></code> element to extend the options for some SVG elements. Maybe this element or something very similar in concept could achieve the URL-swapping without resorting to script. Just something to keep in mind.</em>
</p>
<h3 class="deeplink" id="happy-cuser-client-dev">Happy User, Happy Client, Happy Dev</h3>
<p>In that order. The image used was important in conveying a message and was a vital part of the site’s content, so we made sure it was as accessible and good-looking as it could possibly be, using the awesome that SVG is.</p>
<blockquote>
<p> At Provata, we focus on creating dynamic, interactive, and engaging digital health experiences. So we wanted our marketing website to reflect that. Sara expertly incorporated SVG techniques to deliver a final product beyond our expectations. She is an incredibly knowledgeable and diligent developer. She captured the spirit of our company brilliantly. </p>
<cite>—Alex Goldberg, CEO Provata Health</cite>
</blockquote>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>Working on this site has been great, and my client was one of the best I’ve worked with. Not only do I love the branding and design, but I also love the spirit behind the health program. (You can learn more about it <a href="http://provatahealth.com/">here</a>.) And my absolute favourite part was getting to work with lots of SVG animations using <a href="http://greensock.com/">my favourite animation library</a>. <small>(Thank you, Jack.)</small></p>
<p>The SVG decision-making process is also one I enjoy a lot, even though the amount of options we have can sometimes be overwhelming, and in spite of the fact that we sometimes may need to make certain compromises when choosing one option over the other, <em>and</em> in spite of the lack of full-fledged tools and techniques we got at hand today. I love SVG and enjoy going through the entire process every time.</p>
<p>Hopefully things will get better in the future. The more we use SVG, the more practical, real-life use cases come up with, the more features we need, the more features we should request, and hopefully the more features make it to specs and implementation.</p>
<p>I also learned quite a few designer-y things while working on this project, and plan on sharing more of those in future articles and talks, so stay tuned!</p>
<p>Thank you for reading.</p>
The State of SVG Animation
2015-06-05T00:00:00Z
https://sarasoueidan.com/blog/state-of-svg-animation/
<p class="deck">
The state of SVG animation is changing. CSS, SMIL and JavaScript can be used to animate SVGs. However, SMIL is soon to be deprecated and was never supported in Internet Explorer. CSS animations on SVG elements don’t have the best browser support (yet), not to mention are quite buggy in some browsers. JavaScript is currently the best SVG animation tool. In this article, we'll go over the current and future state of SVG animation, giving you an overview of what you can and can't do, and some advice on what to choose for your specific animation needs.
</p>
Art-Directing SVG Images With The viewBox Attribute: How-To, Notes, Tips and Why We Need A viewBox Property in CSS
2015-05-21T00:00:00Z
https://sarasoueidan.com/blog/svg-art-direction-using-viewbox/
<p class="deck">
The SVG <code>viewBox</code> attribute is easily one of SVG's most powerful features. Mastering this attribute can take your SVG skills to the next level, especially considering that a couple of the main SVG spriting techniques rely on this attribute to work. Because the <code>viewBox</code> attribute can be used to crop and extend the SVG canvas, it can be used for art-directing SVGs—by using it to crop the SVG to the area that you want to display at a time. In this article, I want to go over how to do this, mention some tips that can save you some time doing it, and show the importance of having a <code>viewBox</code> property in CSS, in hopes that this article would serve as a practical use case that helps push <a href="https://lists.w3.org/Archives/Public/www-svg/2013Dec/0080.html">this old SVGWG proposal</a> forward.
</p>
<p>Since I’ve already covered everything you need to know about the <code>viewBox</code> attribute in a <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">previous post</a>, I will assume that you have a basic understanding of how the attribute works and what each of its values stands for. I will be mentioning some of the basics along the way, but I highly recommend you head to my <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">other article</a> and scan it before you move on with this article if you’re not familiar with the attribute.</p>
<h3 class="deeplink" id="overview-of-viewbox-parameters">Quick Overview Of the <code>viewBox</code> Parameters</h3>
<p>The <code>viewBox</code> attribute is used to specify the origin and dimensions of the user coordinate system of an SVG image. All the drawing inside of the SVG is done relative to this system. And since the SVG canvas is conceptually infinite in all directions, you can even draw shapes outside the boundaries of this coordinate system; but the position of those shapes relative to the SVG viewport can also be controlled by the position of the user coordinate system.</p>
<p>The <code>viewBox</code> attribute takes four parameters that specify the position of the origin of the system and its dimensions: <code>x y width height</code>. Initially, this system is identical to the initial viewport coordinate system established by the width and height of the SVG image, and its origin is at (0, 0)—the top left corner of the SVG.</p>
<p>By changing the value of the <code>x</code> and <code>y</code> parameters you change the position of the origin. By changing the <code>width</code> and <code>height</code>, you change the dimensions of the system. This eventually allows you to extend and crop the SVG canvas using nothing but the <code>viewBox</code> attribute. Read along for examples.</p>
<p class="note">IMPORTANT NOTE: Throughout this article I won't be changing the default behavior (scale and position) of the <code>viewBox</code> inside the SVG viewport. Therefore, as per the default behavior of the attribute, the <code>viewBox</code> will scale as much as possible while still be entirely contained inside the viewport, and positioned at its center. Using the <code>preserveAspectratio</code> attribute, you can change the scale and position of the <code>viewBox</code> to your liking, but that is not required for the technique in this article to work, and therefore we won't be getting into that in this article.</p>
<h3 class="deeplink" id="cropping-svg-using-viewbox">Cropping the SVG Using <code>viewBox</code> a.k.a SVG Art Direction Using the <code>viewBox</code> Attribute</h3>
<p>A while back, a client of mine requested that the SVG header photo of his website change on different screen sizes, so that only one portion of the full composition is visible on small screens, a bigger portion is visible on medium screens, and the full composition be visible on large screens. The first idea that crossed my mind when he requested that was to use the <code>viewBox</code> attribute to crop the SVG and only show the portions of the image he wanted on different screen sizes.</p>
<p>By changing the dimensions of the SVG coordinate system and the position of its origin, we can crop an SVG to only show the parts that we want inside the viewport.</p>
<p>Let’s see how that’s done.</p>
<p>Suppose we have the following SVG image in full composition, that we want to crop on smaller screen sizes. The image is a freebie <a href="http://www.freepik.com/free-photos-vectors/house">House vector designed by Freepik</a> and is licensed under Creative Commons Attribution 3.0 Unported License. For the sake of simplicity, we will assume that the image is going to be cropped only once to show one portion on small–medium screens, and the full composition on larger screens, as shown in the image below.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/house-landscape.png" alt="" />
<figcaption>The full composition we are going to crop using the <code>viewBox</code> attribute. The image on the right shows the area of the image that we want to show on smaller screens.</figcaption>
</figure>
<p>Now, there are a few considerations when cropping an SVG by changing the value of the <code>viewBox</code> attribute. We’ll get to these shortly. But first, in order to change the coordinate system so that it matches the dashed rectangular area in the above image, we need to change the value from its initial <code>0 0 800 800</code> parameters by translating the system’s origin and changing the width and height.</p>
<p>But how do you know the new position and dimensions without having to go through a lot of trial and error?</p>
<p>There are a couple of ways. Since we’re already in the graphics editor (Ai, in my example), we can use the editor’s panels to retrieve the positions and dimensions of elements.</p>
<p>There is a reason why I drew that dashed rectangle to wrap the area I want to show on smaller screens: we can retrieve the position and dimensions of this rectangle and use them as values for the <code>viewBox</code>. Using Ai’s Transform panel (see image below), we retrieve these values. By selecting the rectangle and then clicking the Transform link at the top right corner, we get the panel shown in the image below, with the <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code> values that we are going to use.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/illustrator-transform-values.png" alt="" />
<figcaption>The Transform panel in Ai can be used to retrieve the values of the selected rectangle's position and dimensions. </figcaption>
</figure>
<p>As you have probably noticed, the values are not rounded, so we can do that ourselves. Using the above information, we change the <code>viewBox</code> value to: <code>0 200 512 512</code>.</p>
<p><strong>Since the aspect ratio of the new <code>viewBox</code> is the same as the aspect ratio of the SVG viewport (both are square)</strong>, the <code>viewBox</code> is going to scale up and only the selected area will be visible inside of the viewport. The result of changing the <code>viewBox</code> value is:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/new-svg-viewbox.png" alt="" style="max-width: 600px;" />
<figcaption>The new cropped SVG. Only the portion we specified using the <code>viewBox</code> attribute is visible inside of the viewport. The blue border represents the SVG viewport.</figcaption>
</figure>
<p>We need to ask a question here at this point:</p>
<h4 class="deeplink" id="case-of-different-aspect-ratios">What if the aspect ratio of the cropped area (thus, the viewBox) is not the same as the aspect ratio of the SVG viewport?</h4>
<p>Well, in this case, there will be visible overflow. By visible overflow I don’t mean overflow extending beyond the boundaries of the SVG viewport, but overflow relative to the new user coordinate system defined by the <code>viewBox</code>. The following image illustrates the problem.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/diff-aspect-ratio-viewbox.png" alt="" />
<figcaption>If the aspect ratio of the <code>viewBox</code> is different from that of the viewport, and there is content inside the SVG that overflows the user coordinate system, you could end up with something like this.
<p>The black border represents the new user coordinate system, and the blue border is the SVG viewport.</p></figcaption>
</figure>
<p>The black border in the above image on the right is the area defined by the <code>viewBox</code>. As per the default behavior of the <code>viewBox</code> inside the viewport, it has been centered and scaled up as much as possible while remaining fully contained inside the viewport (blue border).</p>
<p>Since the SVG canvas is conceptually infinite in all directions, you can draw outside the boundaries of the user coordinate system, and the content would simply overflow the system as shown in the above image.</p>
<p>If you change the aspect ratio of the SVG viewport (the SVG width and height) so they match those of the <code>viewBox</code>'s, you won’t see that overflow anymore, since the <code>viewBox</code> will scale to fit the viewport as in the previous example.</p>
<p>But, in some scenarios, you may not be able or simply not want to change the aspect ratio of the Svg. An example is if you are using an SVG sprite to display images of a set of avatars on the page. In most cases, the avatars all have a fixed aspect ratio—you won’t be changing the size of each avatar to match the content of the image inside it. Or maybe you’re embedding an icon system and want/need all icons to have the same dimensions all the time.</p>
<p>To clip off any excess (for example, part of another icon in the sprite that is showing inside the viewport), you can use a <code><clipPath></code> to clip that excess out. The clip path would be a <code><rect></code> element that covers the entire <code>viewBox</code> area, and is then applied to the root SVG.</p>
<p>There is, however, one thing to keep in mind here: make sure the <code>x</code> and <code>y</code> attributes of the <code><rect></code> are identical to those of the <code>viewBox</code>, otherwise the <code>rect</code> will be positioned relative to the old/initial system’s origin and you will end up cropping and clipping an unexpected part of the SVG.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>vx vy width height<span class="token punctuation">"</span></span> <span class="token attr-name">clip-path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#clipper)<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content here --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clipper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>vx<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>vy<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Of course, clipping the excess out will mean that you’re still using different aspect ratios and are thus going to end with that extra white space on either side of the content. If the SVG is a continuous scene as in our previous example, this will be unwanted and you will need to adjust the aspect ratio of the viewport. If the SVG is a bunch of icons that you’re showing one at a time inside different viewports, this might not be an issue.</p>
<p>The important thing here to keep in mind that the aspect ratio of the <code>viewBox</code> is best kept the same as that of the viewport; else, you will have to apply a fix to avoid any excess unwanted space inside the SVG.</p>
<hr />
<p>So, the <code>viewBox</code> can be used to crop the SVG and only show parts of it when needed. But how would that be done in a practical example?</p>
<h4 class="deeplink" id="art-directing-svg-for-rwd">Art-Directing An SVG For Responsive Web Design</h4>
<p>Nothing new to add in this section, except the actual process with code. So, suppose you have the above SVG and you want to use it as a header image, for example, and you only want to show the cropped part on small–medium screen sizes and the full composition on bigger screens.</p>
<p>In order to change the value of the SVG viewport’s width and height, we can use CSS. Simple. But to change the value of the <code>viewBox</code>, we currently need to use JavaScript.</p>
<p>Not all SVG presentation attributes are available as CSS properties; only the set of attributes that have CSS property equivalents can be set in CSS. You can see an overview of the set of attributes available as CSS properties in <a href="http://slides.com/sarasoueidan/styling-animating-svgs-with-css#/10">this table</a>. In SVG2, more attributes (like <code>x</code>, <code>y</code>, <code>cx</code>, <code>cy</code>, <code>r</code>, etc.) will be added to the list; but those are the properties we can work with today.</p>
<p>In order to show different parts of the SVG by changing the <code>viewBox</code> value based on different media queries, you can, for example, use Modernizr, check for media query conditions, and then change the <code>viewBox</code> accordingly, in JavaScript. An example of that might look like so:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">// get a reference to the root <svg></span></span><br /><span class="highlight-line"><span class="token keyword">var</span> svgRoot <span class="token operator">=</span> <span class="token operator">...</span><span class="token punctuation">;</span> <span class="token comment">// depends on how you embed and retrieve your SVG</span></span><br /><span class="highlight-line"><span class="token comment">// define your viewBox value(s) to be used</span></span><br /><span class="highlight-line"><span class="token keyword">var</span> vbValue <span class="token operator">=</span> <span class="token string">'0 200 512 512'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">// use Modernizr's media query detection to change the viewBox value</span></span><br /><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span>Modernizr<span class="token punctuation">.</span><span class="token function">mq</span><span class="token punctuation">(</span><span class="token string">'(max-width: 700px)'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> svgRoot<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'viewBox'</span><span class="token punctuation">,</span> vbValue<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token comment">// else if ... etc.</span></span></code></pre>
<p>This works. But wouldn’t it be much more optimal if we could use CSS to do this?</p>
<h4 class="deeplink" id="css-viewbox-property">Using a CSS <code>viewBox</code> Property To Art-Direct SVGs</h4>
<p class="note">DISCLAIMER: <strong>At the time of writing of this article, there is no CSS <code>viewBox</code> property</strong>. This is just an example to demonstrate why such a property would be useful and an example of how I imagine it would be used.</p>
<p>Ideally, we would be able to do something like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 700px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">svg</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token property">viewBox</span><span class="token punctuation">:</span> 0 200 512 512<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token punctuation">}</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* etc. */</span> </span><br /><span class="highlight-line"></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<p>These styles would go inside (or outside) an SVG, and the SVG will then adapt its <code>viewBox</code> according to the viewport size—be it the viewport of the page (in case of inline <code><svg></code>), or the viewport established by the dimensions of whichever element is used to reference the SVG (which would lend us something practically identical to element queries).</p>
<p><strong>This is currently not possible because we don’t have a <code>viewBox</code> property in CSS.</strong></p>
<p>A while back, I asked an SVG spec editor about this, and he said that I could propose it to the SVGWG with a practical use case and example. After some discussion on Twitter, I learned that there already is a fairly old <a href="https://lists.w3.org/Archives/Public/www-svg/2013Dec/0080.html">SVGWG proposal thread</a> that goes a few years back. The initial proposal is still there today, and my hope is that, with a practical use case like this, this proposal could be pushed forward and the property implementation specced at some point in the near future.
If you would like to see the <code>viewBox</code> property in CSS, please help make this happen by pushing this thread forward and commenting on it.</p>
<h4 class="deeplink" id="notes">Things To Keep In Mind When Approaching SVG Art-Direction with <code>viewBox</code></h4>
<p>While working on my client project, it took me literally less than a minute to art-direct the header image the way he wanted. However, we ended up going for three separate SVGs instead of using the same SVG with different viewBoxes on different screen sizes.</p>
<p>The reason we chose to go with three SVGs is that the size of the full composition was too big to serve on mobile—reaching a whopping 100kb+ in size. The initial SVG was around 200kb, and I was able to slash the file size down to half by <a href="https://sarasoueidan.com/blog/svgo-tools">optimizing the SVG</a>, but the resulting size was still too big to serve on mobile, so we ended up using three different images. This is something to keep in mind when art-directing SVGs: performance matters. A lot. So, if your SVG is too big, don’t art-direct it using <code>viewBox</code>.</p>
<p>Now, if you choose to serve three different SVG images, you can do so in one of many possible ways—depending on the way you embed your SVG, and this also depends on what you want or don’t want to do with it.</p>
<p>The ideal way to serve different SVG images would be to use the <code><picture></code> element. Not only does it allow us to provide different SVGs for the browser to choose from without needing JavaScript, but it also enables us to provide <strong>multiple</strong> optimized fallback images for non-supporting browsers (think IE8 and below) as well. <code><picture></code> is great when used with SVG, and you can read all about providing SVG fallback using <code><picture></code> in <a href="https://sarasoueidan.com/blog/svg-picture">this article</a>.</p>
<p>All this being said, <code><picture></code> will not be your best choice if you want to animate or interact with your SVG. Just like an SVG embedded using <code><img></code>, the SVG cannot be styled and animated unless the styles and animations are defined inside the <code><svg></code> file, the SVG cannot be scripted (for security reasons), and any interactions (CSS or JS) — like hover, for example — will not work.</p>
<p>So, as I always say: SVG provides us with a lot of options to do almost everything; you need to weigh things, prioritize, sometimes maybe even make compromizes, and pick your best route based on that. But never compromise performance in favor of development convenience.</p>
<hr />
<p>Before we finish up, and since we’re on the subject of changing the SVG canvas’ size using the <code>viewBox</code> attribute, let’s take a look at another example where we can leverage the power of this attribute to save us some time and effort when working with SVG.</p>
<h3 class="deeplink" id="extending-svg-canvas-using-viewbox">Extending the SVG Canvas using <code>viewBox</code></h3>
<p>Just like the <code>viewBox</code> attribute can be used to crop an SVG, it can be used to extend the SVG canvas as well.</p>
<p>A few weeks ago I created <a href="https://sarasoueidan.com/tools/circulus/">a tool that allows you to generate circular menus in SVG</a>. I created a few examples to show how a generated menu could be animated using JavaScript. The demos are embedded on the app page using the <code><object></code> element. The boundaries of the <code><object></code> define the boundaries of the SVG viewport, and anything that lies outside these boundaries is considered overflow and will be hidden by default.</p>
<p class="note">Note that the phrase "outside these boundaries" refers to content inside the SVG that is still drawn on the infinite SVG canvas, but that extends beyond the finite rectangle defined by the viewport.</p>
<p>The menus are created such that the size of the SVG is just big enough to contain the menu, not more, to avoid any excess and unwanted white space around the menu.</p>
<p>I applied a staggering, bouncing animation to one of the menus as an example of how the menu can be animated. The bouncing effect “stretched” the menu items, and this led to the items being cut off while they animated.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/items-overflow-recording.gif" alt="Screen recording showing how the menu items get cut off when they are animated with a bouncing animation." />
<figcaption>
Initially, and since the SVG viewport defined by the <code><object></object></code> element is just as big as the menu itself, the bouncing effect on the menu items results in these items being cut off when animated.
</figcaption>
</figure>
<p>The staggering bouncing animation is applied to the items such that it will scale an item from zero (items are initially not visible, scaled down) to 100% using a bouncing timing function, the effect of which will be scaling the item <em>beyond</em> 100% right before it is scaled back to 100%. This effect causes the item to scale up beyond the boundaries of the SVG and hence get cut off.</p>
<p>The following image shows the result of scaling the menu item up beyond the boundaries of the <code><object></code> used to embed it (grey border).</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/circular-menu-overflow-illustration.png" alt="Image showing the result of applying a bouncing scale animation to the menu item, resulting in the item being cut off." style="max-width: 600px;" />
<figcaption>
Illustration showing the menu item overflowing the boundaries of the SVG viewport when it is scaled up. The grey border represents the border of the SVG viewport (the <code><object></object></code>).
</figcaption>
</figure>
<p>Setting <code>overflow: visible</code> on the <code><object></code> does <em>not</em> fix this, because <code><object></code> is practically similar to an <code><iframe></code> in behavior. What we need to do is <em>extend</em> the SVG canvas <em>inside</em> the viewport created by the <code><object></code> so that the scaled items have enough space to “bounce” without exceeding its boundaries. We can do this using the <code>viewBox</code> attribute.</p>
<p>To extend the SVG canvas, you simply increase its dimensions. So, instead of 500px by 250px—the original dimensions of the SVG menu, we use 700px by 350px; this will increase <strong>the height of the canvas visible inside of the viewport</strong> by 100px, and its width inside of the viewport by 200px. I chose these values based on how much the menu item is being scaled up in the bounce effect. Depending on your SVG and what you’re working on, these values might be different.</p>
<p>Now, to make sure the menu remains centered inside of the viewport, we’re going to shift the position of the coordinate system by 100 pixels in the negative direction (upwards and to the left). Applying this shift to the origin of the coordinate system is practically the same as applying a translation transformation to the menu inside of the system. The result will be that the menu remains centered inside of the viewport.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/circular-menu-overflow-fix-illustration.png" alt="Image showing the result of extending the SVG canvas to provide more space for the menu items to be animated without being cut off." style="max-width: 700px;" />
<figcaption>
In this illustration, the blue borders represent the border of the SVG viewport (the <code><object></object></code>). The grey borders in this image show the initial dimensions of the user coordinate system. The blue numbers and arrows represent the extension of the coordinate system inside of the viewport.
</figcaption>
</figure>
<p>By extending the dimensions of the user coordinate system, you are increasing the area of the canvas visible inside of the viewport. The result of doing this will also be that the contents of the canvas will look slightly smaller—depending on how much you increase the canvas. In the case of the menu, the result was perfectly acceptable.</p>
<p>The following screen recording shows the result of extending the SVG canvas and how the animation now looks inside the buondaries of the SVG.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/items-overflow-fix-recording.gif" alt="Screen recording showing the result of extending the SVG canvas using the viewBox attribute, thus preventing the menu items from being cut off once animated." />
<figcaption>
Once the SVG canvas has been extended and the menu items have enough space to scale up, they are no longer cut off when the bouncing effect is applied.
</figcaption>
</figure>
<p>Changing four values inside the <code>viewBox</code> attribute to extend the SVG canvas was all that was needed to troubleshoot and solve the issue of the items being cut off. Now that’s pretty powerful, isn’t it?</p>
<h3 class="deeplink" id="wrapping-up">Wrapping Up</h3>
<p>The <code>viewBox</code> attribute is awesome. It is literally SVG on steroids. By using this attribute, you can save a lot of time when working with SVG, troubleshoot SVG quickly without having to resort to a graphics editor, and, all in all, feel more comfortable editing SVG by hand.</p>
<p>I highly recommend you learn all about this attribute if you haven’t already, <a href="http://sarasoueidan.com/demos/interactive-svg-coordinate-system/index.html">play with its values</a>, and then leverage its power in your work. And if you do decide to use it to art-direct SVGs, don’t forget to keep performance in mind.</p>
<p>One of the main objectives of this article was to also shed some light on the importance of having a <code>viewBox</code> property in CSS, so if you’re convinced that this property is needed, please take the time to vote on / respond to the SVGWG thread and support the request.</p>
<p>Thank you very much for reading! :)</p>
An Introduction To Graphical Effects in CSS
2015-04-29T00:00:00Z
https://sarasoueidan.com/blog/css-graphics/
<p class="deck">
In this article, we will take a deep introduction into CSS’s graphical effects—specifically, CSS Filters and Compositing and Blending capabilities. We will go over the properties for each, their different values, and usage examples and some of the graphial effects that can be created using nothing but a few lines of CSS.
</p>
I Won A Web Platform Award
2015-04-22T00:00:00Z
https://sarasoueidan.com/blog/oreilly-web-platform-award/
<p class="deck">Today, O'Reilly's Fluent Conf is taking place in San Francisco, California. And as part of the conference, the O'Reilly Web Platform Awards were announced. Apparently, I was nominated for an award and, according to the co-chairs of the conference, I got the most amount of nominations and eventually won an award.
</p><figure>
<img src="https://sarasoueidan.com/assets/images/web-platform-award.jpg" alt="The Web Platform award." />
</figure>
<blockquote>
O’Reilly Web Platform Awards recognize individual contributors who have demonstrated exceptional leadership, creativity, and collaboration in the development of JavaScript, HTML, CSS, and the supporting Web ecosystem. The nomination process is open to the entire web community and all entries will be judged by the Fluent program committee.
</blockquote>
<p>This is my first time ever winning a web award, and I feel privileged to have won it from such a prestigious company.</p>
<p>Simon sent me the “Congratulations, you won a web platform award!” email a couple of weeks before the awards were announced. My first reaction when I read the email was: “Okay this must have gotten into my inbox by mistake.” So I ended up responding to his message asking him if the email was really intended for <em>me</em>. He said that it was. I couldn’t believe it for a while and it took me some time to let the idea sink in that I had actually won an award.</p>
<p>I had <em>no clue</em>.</p>
<p>I love doing what I do and sharing what I know. I find great pleasure in helping others with what I know and always have—ever since I can remember. I really don’t know what to say except <strong>Thank You</strong> to each and every one who nominated me, and to the committee who voted, and to O’Reilly Fluent for this great award. It is such a wonderful and overwhelming feeling to realize that your work has been recognized by the very community you’re part of and have been contributing to. To know that the community appreciates your work and finds it useful is one of the best feelings ever.</p>
<p><strong>Thank you</strong>.</p>
A Primer To Background Positioning In CSS
2015-03-24T00:00:00Z
https://sarasoueidan.com/blog/css-background-positioning/
<p class="deck">
An article in which we take a deep dive into CSS’s background positioning properties with visual explanations and examples. Did you know that the CSS <code>background-position</code> property accepts edge offset values? That is, you can position a background image relative to <em>any</em> edge—not just top and left—and specify the offset relative to that edge using a length value. In this article, we will learn all about that, and more.
</p>
Building a Circular Navigation with SVG
2015-03-09T00:00:00Z
https://sarasoueidan.com/blog/building-a-circular-navigation-with-svg/
<p class="deck">
Last week, I released <a href="https://sarasoueidan.com/tools/circulus">CIRCULUS.SVG</a>—the SVG circular menu generator. In this article I want to go over why SVG is better suited for creating this kind of UI element, and give you and overview of the SVG code for creating the menu items using SVG elements and transformations.
</p>
<p>Note that, unlike my usual tutorials, we will not be going over a detailed how-to, but only an overview of the concepts behind this. Creating the menu in detail would require lots of maths and digging into the SVG path data syntax which deserves an article of its own; so, for the sake of brevity, I will <em>not</em> be digging into either of these, but will go quickly over the concepts.</p>
<p>Now, to draw the sectors, you can, of course, literally <em>draw</em> them in a graphics editor like Illustrator, Inkscape or Sketch, and then export your graphic as SVG, make it interactive, and embed it. However, since the title says “building”, we’re going to go over how to draw these items with code.</p>
<p>If you’re only interested in the end result—a usable circular menu, you can head to the generator and create your own menu there. Otherwise, let’s start with why SVG is better than CSS for creating circular menus.</p>
<h3 class="deeplink" id="svg-vs-css">SVG vs. CSS</h3>
<p>Over a year ago, I wrote <a href="http://tympanus.net/codrops/2013/08/09/building-a-circular-navigation-with-css-transforms/">an article</a> over at Codrops about using CSS Transforms to create a circular navigation. Even though the technique works, it comes with a couple of browser bugs & inconsistencies, not to mention that it’s practially hacky—we’re bending the rectangular box model to create the menu items by skewing and rotating the list items and cutting them off by hiding the overflow on their container. The innards of the list items need to be “un-skewed”, and everything is positioned absolutely.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/css-circular-nav-demo.gif" alt="Screen recording showing the steps needed to create a circular menu in CSS" />
<figcaption>
Screen recording showing the steps needed to create a circular menu in CSS. The interactive demo can be found in the Codrops article.
</figcaption>
</figure>
<p>Placing content other than icons inside the menu can get difficult depending on the type of content. And finally, to make the menu repsonsive, you’re gonna need to use media queries and adjust the different sizes used for different viewport widths (and/or heights!).</p>
<p>With SVG, on the other hand, it’s very different.</p>
<hr />
<p>Shapes in SVG are marked up as semantic, fully-accessible XML elements like <code><rect></code>, <code><circle></code>, <code><ellipse></code> and <code><path></code>. And with the different drawing elements available, SVG makes for the perfect candidate for drawing non-rectangular shapes and elements. And to top it off, SVG items can be styled and scripted and hence are completely interactive. That’s exactly what we need for our circular menu.</p>
<p>Since we are creating circular slices — a.k.a <strong>sectors</strong> (symbol: ⌔) — as menu items, we will use the <code><path></code> element to mark each slice up, since SVG’s path commands will allow us to draw the slices in a more straightforward manner using the line and arc commands available.</p>
<p>Drawing the menu items in SVG is much, much more straightforward. Here is an interactive demonstration showing how the items are drawn and positioned inside the menu. Play the animation to see the demonstration.</p>
<p data-height="500" data-theme-id="3617" data-slug-hash="2e56afeaa278c90141853ff805da1a06" data-default-tab="result" data-user="SaraSoueidan" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/2e56afeaa278c90141853ff805da1a06/">Building A Circular Menu With SVG #2</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://assets.codepen.io/assets/embed/ei.js"></script>
<p>Now <em>that</em> is definitely better than the steps taken in CSS, isn’t it? The SVG <code><path></code> comes with a bunch of commands for drawing lines and arcs. Let’s take a closer look at the commands and parameters used to draw our menu items.</p>
<h3 class="deeplink" id="drawing-items-using-svg-path">Drawing A Menu Item Using SVG <code><path></code></h3>
<p>We’re going to need some data to draw our sectors. We will then pass this data to the <code><path></code> commands as parameters that define the shapes we’re drawing.</p>
<p>A sector is defined by three points, a radius, and an angle. In order to draw a sector using the SVG <code><path></code> element, you need to know the coordinates for the three points. Then, using the path commands, we are going to <strong>move to</strong> the center of the circle (the first point), draw a <strong>line to</strong> the circle’s circumference (second point), then draw an <strong>elliptical arc</strong> from the second point to the position of the third point, and then <strong>close the path</strong> by drawing a line back to the center.</p>
<p>The following is an interactive illustration showing how the path will be drawn. Click the button to start the demonstration.</p>
<p data-height="700" data-theme-id="3617" data-slug-hash="24de844274fb139d7eb1702783c9076d" data-default-tab="result" data-user="SaraSoueidan" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/24de844274fb139d7eb1702783c9076d/">24de844274fb139d7eb1702783c9076d</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://assets.codepen.io/assets/embed/ei.js"></script>
<p>The three colored points are the points required to draw the sector. So let’s do some simple calculations to determine the coordinates of these points.</p>
<h3 class="deeplink" id="determining-point-coordinates">Determining Point Coordinates</h3>
<p>In order to build the circular menu, we are going to start with a square SVG canvas that has <strong>500px by 500px</strong> dimensions. So, the menu will be centered inside it. The center of the circle will be at the center of the square. The radius will be <strong>250px</strong>. So the blue dot in the above demo (point A) will have the coordinates (250px, 250px), and the orange one (point B) will be positioned at (500px, 250px).</p>
<p>To determine the coordinates of the third point ©, we need the value of the angle (that is determined based on the number of menu items); then, using the given data and the values of the angle’s sine and cosine, we can get <strong>the polar coordinates</strong> of the third point. The <strong>y</strong> value of the pink dot in the polar coordinate system is equal to <strong>sin(angle)</strong> multiplied by the radius <strong>r</strong>. The <strong>x</strong> value is equal to the <strong>cos(angle)</strong> mutiplied by the radius <strong>r</strong>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-circ-menu-points.svg" alt="" />
<figcaption>
The <strong>y</strong> polar coordinate value of the pink dot in the polar coordinate system is equal to <strong>sin(a)</strong> (h/r) multiplied by the radius <strong>r</strong>. The <strong>x</strong> polar coordinate value is equal to the <strong>cos(a)</strong> (w/r) mutiplied by the radius <strong>r</strong>.
</figcaption>
</figure>
<p>For the path data, we need the <strong>cartesian coordinates</strong> of the point, which means that we now need to convert the polar x and y coordinates we have to cartesian coordinates. Using a simple math conversion we can transform those coordinates into cartesian coordinates <strong>which will represent the coordinates of the pink dot in the SVG canvas</strong>. The conversion formula looks something like this (speaking in JS):</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">//polar to cartesian coordinates conversion</span></span><br /><span class="highlight-line"><span class="token comment">//knowing the value of your angle in degrees..</span></span><br /><span class="highlight-line"><span class="token comment">//get value of the angle in radians</span></span><br /><span class="highlight-line">angleInRadians <span class="token operator">=</span> <span class="token operator">-</span>angleInDegrees <span class="token operator">*</span> Math<span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">/</span> <span class="token number">180.0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">//get the cartesian x coordinate (centerX = x coordinate of the center of the circle == 250px in our case)</span></span><br /><span class="highlight-line">x <span class="token operator">=</span> centerX <span class="token operator">+</span> radius <span class="token operator">*</span> Math<span class="token punctuation">.</span><span class="token function">cos</span><span class="token punctuation">(</span>angleInRadians<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">//get the cartesian y coordinate (centerY = y coordinate of the center of the circle == 250px in our case)</span></span><br /><span class="highlight-line">y <span class="token operator">=</span> centerY <span class="token operator">+</span> radius <span class="token operator">*</span> Math<span class="token punctuation">.</span><span class="token function">sin</span><span class="token punctuation">(</span>angleInRadians<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Once you have all coordinates, you are ready to draw the sector.</p>
<h3 class="deeplink" id="drawing-the-sector">Drawing The Sector's Lines And Arc</h3>
<p>Using the SVG <code><path></code> element, each sector can be drawn using one line of SVG:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token entity named-entity" title="<">&lt;</span>path fill="transparent" stroke="#111" stroke-width="2" d="M250,250 l250,0 A250,250 0 0,0 466.5063509461097,125 z" /<span class="token entity named-entity" title=">">&gt;</span></span></code></pre>
<p>The part we’re interested in is the content of the <code>d</code> (= data) attribute; it is where our coodinates will come in use. Here is a colored breakdown of the path data:</p>
<p>
<code>
<strong>M</strong><span style="color: #32BAFC">250,250</span> <strong>l</strong><span style="color: orange">250,0</span> <strong>A</strong><span style="color: #aaa">250,250 0 0,0</span> <span style="color: deepPink">466.5063509461097,125</span> <strong>z</strong>
</code>
</p>
<p>We’re using four path commands here: <strong>M</strong>, <strong>l</strong> (small L), <strong>A</strong> and <strong>z</strong>.</p>
<p>First, <strong>move to (M)</strong> the point of coordinates 250,250—the center of the circle.</p>
<p>Next, draw a <strong>line to (l)</strong> the point that is at 250,0 <strong><em>relative to the current position</em></strong>. In other words, when we move to the orange dot, we are not using that point’s coordinates. We are calculating the horizontal and vertical distance of this point relative to the current position—which in this case is the center of the circle. You can, however, use the point’s coordinates if you use the <strong>L</strong> command (capital letter), which draws a line using absolute coordinates instead of relative ones.</p>
<p>Okay so, move from the center 250 units to the right, drawing a line in that direction.</p>
<p>Next, draw an <strong>elliptical arc (A)</strong> defined by: <strong>250,250 0 0,0</strong> (we’ll get back to these shortly), from the current position to the point at 466.5063509461097,125—which are the cartesian coordinates of the pink dot. The capital letter <strong>A</strong> command will draw an arc using absolute values; that is, it will draw an arc from the current position to the position you specify in the coordinates, and these coordiates will be absolute, <em>not</em> relative to the current position.</p>
<p>Then, <strong>close the path (z)</strong>: a line is drawn from the pink dot back to the center, and the sector is complete.</p>
<p>But what is that <strong>250,250 0 0,0</strong> part all about?</p>
<p>The elliptical arc command takes in a few parameters: <strong>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)</strong>.</p>
<p>For our circular menu, the <strong>250,250</strong> part determines the horizontal and vertical radii (<strong>rx ry</strong>). Both values are equal since we are drawing a <em>circular</em> sector, not an elliptical one. You set these to be equal to the radius of the circle you are working on.</p>
<p>For the sake of brevity, I’ll skip the next three parameters for now. What you need to know is that, for our circular menu, you need to set these three parameters to zero since we are drawing small circular arcs.</p>
<p>Finally, the coordinates of the point to which the arc will be drawn are provided (<strong>x y</strong>).</p>
<p>With one sector drawn, the others follow the same way. Draw as many sectors as you need. Then, the remaining sectors are rotated by the necessary angle to position them along the circle as we saw in demonstration from the previous section.</p>
<p>Since CSS transforms on SVG elements are not supported in IE, I’ve used <a href="https://sarasoueidan.com/blog/svg-transformations">SVG’s native transformations</a> to rotate the items. The <code>transform</code> attribute takes three parameters: the angle of rotation, and the x and y position of the center of rotation. The center of rotation is the center of the circle at (250px, 250px), and the angle of rotation is calculated based on the number of menu items you choose in the GUI and whether the menu is a full circle or a semi circle.</p>
<h3 class="deeplink" id="adding-icons">Adding Icons To The Menu Items</h3>
<p>Since each icon could be more than just an icon—for example, an icon and a label, or just a label, it is best if we had a “wrapper” for whatever the contents of each item’s icon will be. The first thing that comes to mind in this case is using a group element: <code><g></code>. However, <code><g></code> has its limitations as it does not come with a <code>viewBox</code> attribute, nor does it create a coordinate system for its content to be positioned inside. The next logical option is using an <code><svg></code> element as a wrapper.</p>
<p>The icons <em>could</em> be wrapped in <code><svg></code> elements which will create a coordinate system for the icon’s content. That being said, I chose not to go with this option because it would have required you to get your hands dirtier with the code whenever you wanted to modify or change the icons. I wanted to make it as simple as possible. For that reason, I chose the next best option: <code><symbol></code> and <code><use></code>.</p>
<p><code><symbol></code> accepts a <code>viewBox</code> attribute, and <code><use></code> accepts <code>width</code> and <code>height</code> attributes that serve as the viewport for the <code>viewBox</code> specified on the <code><symbol></code>. Thus, combined, <code><symbol></code> and <code><use></code> provide us with what <code><svg></code> would have provided us, plus a way to separate icon definitions from their actual use throughout the menu. Perfect.</p>
<h3 class="deeplink" id="positioning-the-icons">Positioning The Icons</h3>
<p>We don’t have relative positioning in SVG that allows us to position an element relative to another element, but we can use <a href="https://sarasoueidan.com/blog/nesting-svgs">nested SVGs</a> to work around that. That being said, nesting SVGs to position the icons “relative” to the sectors (or: relative to a common container, to be more accurate) would have been overkill given that we I could do it another way.</p>
<p>My objective was to position the icons at the center of the sections and rotate them by an angle so that they look as if they are rotated <em>with</em> the sector. Visually speaking, the icons would be positioned along a virtual line that bisects the sector’s angle, and does not extend beyond the chord joining the orange and pink dots from the previous section’s illustration.</p>
<p>Using the SVG DOM API, we can translate the above logic into code by first determining the virtual alignment line for the icon in the middle of the sector, the maximum length of that line which we can specify after getting the point on the chord where the virtual line and the chord would intersect, and then using the SVG <code>getPointAtLength()</code> method to specify where on that line the icon should be positioned. Then, what is left after that is simply rotating the icon by half the angle of the sector and nudging it a little bit so that its center is positioned at the point on the line that we want.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svg-menu-icons-alignment.svg" alt="" style="max-width: 600px" />
<figcaption>Illustration showing icons positioned along a virtual line inside each menu item's sector.</figcaption>
</figure>
<p>The two black dots in the above illustration show the position of the icon along the line (that we can get using <code>getPointAtLength()</code>) and the point of intersection of the line with the sector’s chord. The range control in the app’s GUI that allows you to change the position of the icon inside each item actually changes the result of <code>getPointAtLength()</code>, making sure it does not exceed the point of intersection with the chord.</p>
<h3 class="deeplink" id="linking">Linking In The Menu</h3>
<p>Each menu item is made up of the path representing the sector shape and a <code><use></code> element referencing a <code><symbol></code>. These two elements are wrapped in an anchor element: <code><a></code> to make an item clickable.</p>
<p>Just like HTML <code><a></code> elements, an SVG anchor also has <code>href</code> and <code>title</code> attributes, with one important difference: namespacing. Before the <code>href</code> and <code>title</code> parts, you need to add the <code>xlink</code> namespace such that the link would be marked up like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token entity named-entity" title="<">&lt;</span>a xlink:href=".." xlink:title=".."<span class="token entity named-entity" title=">">&gt;</span> <span class="token entity named-entity" title="<">&lt;</span>!-- item contents --<span class="token entity named-entity" title=">">&gt;</span> <span class="token entity named-entity" title="<">&lt;</span>/a<span class="token entity named-entity" title=">">&gt;</span></span></code></pre>
<p>Additionally, the menu generator adds the <code>role</code> and <code>tabindex</code> attributes for accessibility. And that’s pretty much all you need for the items.</p>
<h3 class="deeplink" id="styling-and-interactivity">About Styling And Interactivity</h3>
<p>In the CSS version of this menu, pointer events were buggy in some browsers and <code>z-index</code> was needed to handle stacking the elements on top of each other. With SVG, and because of the nature of elements in SVG, the pointer events are restricted to each shape without having to do anything extra. Since each shape is independent from the other and they do not overlap, no stack handling is required. Everything just works as you’d expect it to.</p>
<p>Moreover, the SVG elements can be selected and styled in CSS. In order to make styling the elements and icons easier, I avoided adding any unnecessary presentation attributes.</p>
<p>You can interact with the menu items and animate them independently using CSS or JavaScript. The app comes with a guide that includes a few animated examples using JavaScript. I chose the latter over CSS for browser compatibility because, again, IE does not support CSS transformations on SVG elements yet.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>SVG is very powerful, and SVG paths are one of the most powerful of its features. The very nature of SVG elements gives us more flexibility for creating and animating non-rectangular UI elements. And the fact that these elements can be drawn while remaining semantic and fully accessible gives SVG an edge for creating visually and functionally superior interfaces.</p>
<p>I hope you found this article useful. Thank you for reading.</p>
Extending the Color Cascade with the CSS currentColor Variable
2015-02-24T00:00:00Z
https://sarasoueidan.com/blog/currentcolor/
<p class="deck">
If you use Sass or LESS, then you probably already use variables in your style sheets and know how useful they are. If you don’t use a preprocessor, then you might be curious what the fuss is all about and why variables are so popular and how they can be useful. In this article, we’re going to get an overview of why variables are useful, and get acquainted with one particular variable: currentColor.
</p>
Better SVG Fallback and Art Direction With The <picture> Element
2015-02-15T00:00:00Z
https://sarasoueidan.com/blog/svg-picture/
<p class="deck">Besides using an SVG as a background image in CSS, you can serve SVG foreground images in HTML using one of several embedding techniques, each of which has its advantages and use cases. Unless you’re in need of interactivity or external styling, <code><img /></code> is the standard way for loading an SVG image, but it has one disadvantage: you currently need JavaScript to provide fallback and/or change the image source for art direction. In this post, I’ll talk about a better way to do that, using the <code><picture></picture></code> element.</p>
<p>This is not a primer to using <code><picture></code>. There are a lot of great resources in the wild that contain everything you need to know about the <code><picture></code> element, so if you’re not familiar with it, refer to the last section of the article for a list of resources to learn all about it. That being said, the article does not require any special or strong <code><picture></code> skills, as the examples are mostly self-explanatory as you will see.</p>
<p class="note">
This article is also <a href="http://css-live.ru/articles/obespechenie-luchshej-rezervnoj-podderzhki-svg-i-upravlenie-oformleniem-s-pomoshhyu-elementa-picture.html">available in Russian</a>.
</p>
<h3 class="deeplink" id="the-current-img">The Current <code><img /></code></h3>
<p>Before getting into why <code><picture></code> is a great option for SVG, let’s lay down (an overview of) the limitations and disadvantages of using <code><img></code> for responsive SVG work.</p>
<p>Normally, if you load an SVG using an <code><img></code> tag, you can provide fallback and change the source of the image on different viewport sizes using feature detection and media queries in JavaScript. My choice for both would be to use <a href="http://modernizr.com/">Modernizr</a> for both; that is, unless you’re only serving one image, in which case adding Modernizr might be overwork, and something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo.svg<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onerror</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token keyword">this</span><span class="token punctuation">.</span>src<span class="token operator">=</span>logo<span class="token operator">-</span>fallback<span class="token punctuation">.</span>png<span class="token punctuation">;</span><span class="token keyword">this</span><span class="token punctuation">.</span>onerror<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>…would be simpler and faster.</p>
<p>Using Modernizr, you can detect browser support for SVG, and provide an alternative image <code>src</code> for when SVG is not supported. The alternative image URL can be stored in a custom data attribute. This approach is particularly useful for when you have multiple images on the page whose <code>src</code> you need to switch.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo.svg<span class="token punctuation">"</span></span> <span class="token attr-name">data-fallback</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo.png<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Using Modernizr, you can detect whether or not the browser supports SVG and then take necessary steps to provide the fallback based on the test result:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Modernizr<span class="token punctuation">.</span>svg<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// fetch fallback and replace img src with it</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can also use Modernizr’s media query detection to change the img src on based on viewport width for when you want to do art direction and not load the same big SVG on smaller screens:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span>Modernizr<span class="token punctuation">.</span><span class="token function">mq</span><span class="token punctuation">(</span><span class="token string">'(max-width: 640px)'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// replace image source with the smaller SVG source</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You don’t need to store any URLs in data attributes in this case if you are following a specific naming convention for your images. For example, if your images are named <code>view-small.svg</code>, <code>view-big.svg</code>, you can just replace the <code>view-*</code> part with the appropriate one in the JavaScript and be done with it.</p>
<p>Now, if you want to provide a PNG or JPEG fallback for your SVG <em>and</em> also provide different sizes of that fallback image to match the viewport size, Modernizr will also do, but it will get slightly more complicated. And the most important part is: you need JavaScript to do it.</p>
<p>With the <code><picture></code> element, we can do all that and more, without JavaScript. Well, kind of. Read on.</p>
<h3 class="deeplink" id="the-bigger-picture">The Bigger <code><picture></picture></code></h3>
<p>The <code><picture></code> element provides us with a better JavaScript-less way to change the image we are serving based on different media queries <em>and</em> a for providing fallback for non-supporting browsers (or browsers that can’t load the SVG for any reason).</p>
<h4 class="deeplink" id="loading-and-providing-fallback">Loading An SVG and Providing Fallback For Non-Supporting Browsers</h4>
<p>Let’s start with fallback first. Providing fallback for browsers that can’t load the SVG is as simple as wrapping your <code><img></code> fallback in a <code><picture></code> element, and referencing your SVG in a <code><source></code> element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/fallback.png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Image description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code><picture></code> element is just a wrapper for the elements used to load the image(s) you want and for the fallback provided with the <code><img></code> element. The <code><img></code> element is <em>required</em> for <code><picture></code> to work and is used to provide backwards compatibility for browsers that don’t support <code><picture></code> or, like our case here, browsers that can’t load or don’t support the the image(s) [type] you load in the <code><source></code> element.</p>
<p>The <code><source></code> element is where we specify the image(s) we want. We’re specifying the type of the image we want (SVG) in the <code>type</code> attribute, and providing the URL to that image in the <code>srcset</code> attribute.
And that’s all there really is to it; this is how simple it is to provide fallback for an SVG image using <code><picture></code>—no JavaScript is needed.</p>
<p>You can take this even further and provide multiple fallback images that take screen resolution into account; to do that you can specify those images using the <code>srcset</code> attribute on the <code><img></code>. For example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/logo.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/logo-1x.png<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/logo-2x.png 2x, path/to/logo-3x.png 3x<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Logo description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>The browser can then choose the image it finds appropriate based on the screen resolution. This is useful for when you are serving the same image size (for example, a one-size logo) but want to provide 2x and 3x versions for higher resolutions.</p>
<p>But if you want you can take it even <em>further</em>. With the help of the <code>sizes</code> attribute, you can use media queries on the <code><img></code> to change the fallback image size on different screen sizes, providing a bigger image for bigger screens and a smaller one for small screens.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/banner.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span><br /><span class="highlight-line"> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width: 640px) 80vw, 100vw<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>banner-300.jpg 300w,<br /><span class="highlight-line"> banner-400.jpg 400w,</span><br /><span class="highlight-line"> banner-700.jpg 700w,</span><br /><span class="highlight-line"> banner-1200.jpg 1200w,</span><br /><span class="highlight-line"> banner-1600.jpg 1600w,</span><br /> banner-2000.jpg 2000w<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>banner-default-fallback.jpg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Banner description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>What we’ve done here is we told the browser in the <code>sizes</code> attribute what size our image will occupy on the page. In this case, if the width of the viewport is 640px or more, the image will be 80% the width of the viewport, and 100% the width otherwise.</p>
<p>Then, in the <code>srcset</code> attribute, we provided the browser with a list of images—they are all the same image, but in different sizes. Based on the sizes specified in <code>sizes</code>, the browser will pick the best fit among these images and display it.</p>
<p>If a browser does not support <code>srcset</code> on <code><img></code>, it will simply display the fallback specified in the <code>src</code> attribute. For a detailed explanation of how this works, refer to <a href="http://alistapart.com/article/responsive-images-in-practice">this article</a> over at A List Apart.</p>
<h4 class="deeplink" id="art-direction">Art Direction: Loading a Different SVG on Different Screen Sizes</h4>
<p>The <code><source></code> element we use to specify the image(s) we want comes with another attribute: <code>media</code>. This attribute provides us with the same flexibility we have for changing background images in CSS using CSS media queries, by allowing us to pair image sources with layout conditions (the media queries) right in the source code.</p>
<p>Since we’re serving an SVG image, we don’t need to serve multiple versions of the image for different screen resolutions because of the infinitely scalable nature of SVG which makes it look great on any resolution. (Yay!)</p>
<p>But if we have an SVG that we’re serving on desktop—for example, a wide header image, this image could be hundreds of kilobytes in size. Serving the same big image for small screens might not be the best idea if you look at it from a performance angle. Moreover, maybe you just <em>don’t want</em> to serve the same image on smaller sizes, but a “cropped” version of that image. I recently worked on a client project that required just that. Not only did my client want different images on smaller sizes, but the full composition was more than 100KB in size, which is obviously too much to serve on mobile devices, so we served cropped versions of that image.</p>
<p>In such a case, you can specify different SVGs to load on different media conditions using the <code>media</code> attribute on the <code><source></code>. In the <code>media</code> attribute, you specify the media conditions similar to how you do it in CSS media queries.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 640px)<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>header--small.svg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 1024px)<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>header--medium.svg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /><span class="highlight-line"> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>header--full.svg<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>header--default-fallback.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Header description..<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span></code></pre>
<p>Of course, you can specify different fallback images for different resolutions and sizes, similar to what we did in the previous section. For the sake of brevity, I’m going to skip that step in this section, but you get the picture. (See what I did there?)</p>
<p>You can also specify multiple sizes of each SVG image and let the browser pick the one it finds best, like we did for the fallback image before. However, due to the scalable nature of SVG, this might not be necessary.</p>
<p>As a matter of fact, the options the <code><picture></code> element comes with cover practically any scenario. <a href="https://dev.opera.com/articles/responsive-images/">This article</a> on the dev.opera blog covers a lot of these use cases with practical examples and snippets to help you get started.</p>
<hr />
<p>So, you see, with the <code><picture></code> element, we no longer need to use JavaScript to provide fallback and/or change the image based on different media conditions. Well, kind of…</p>
<h3 class="deeplink" id="browser-support-and-polyfilling">Browser Support and Polyfilling</h3>
<p>At the time of writing of this article, browser support for <code><picture></code> isn’t at its best, but it is getting better. A lot of smart people are working on <code><picture></code> implementation across browsers. Keep an eye on <a href="http://caniuse.com/#feat=picture">the compatibility table over at CanIUse.com</a> to stay up-to-date on browser support in the future.</p>
<p>In the meantime, and until browser support becomes more decent, you can use a JavaScript polyfill for non-supporting browsers. So yes, we do need JavaScript at the moment, but the code you write will be future-proof and all you need to do in the future when support gets better is to remove the polyfill, and your code will work without it as expected. Using <code><img></code> you’d need to do much more, or, at least, just keep using Javacript.</p>
<p>The <a href="http://scottjehl.github.io/picturefill/">Picturefill</a> polyfill by the folks at the Filament Group is the current de facto for cross-browser <code><picture></code> support today. The polyfill homepage contains extensive documentation on how to use the polyfill and tips on using <code><picture></code> in general along with general patterns.</p>
<p>Using the polyfill is as simple as including the script in your page’s <code>head</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>picturefill.js<span class="token punctuation">"</span></span> <span class="token attr-name">async</span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>async</code> attribute tells the browser that it can load picturefill asynchronously, without waiting for it to finish before loading the rest of the document. According to the Picturefill documentation, If you add this attribute, you’ll need to add a line of script before the script tag as well to allow older browsers to recognize <code>picture</code> elements if it encounters them in the page before Picturefill has finished loading.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> <span class="token comment">// Picture element HTML5 shiv</span></span><br /><span class="highlight-line"> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span> <span class="token string">"picture"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>picturefill.js<span class="token punctuation">"</span></span> <span class="token attr-name">async</span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you are <a href="http://www.paulirish.com/2011/the-history-of-the-html5-shiv/">familiar with HTML5 Shiv</a>, then you already know what this line is needed for. As a matter of fact, if you are already including a recent version of the HTML5 Shiv (sometimes packaged with Modernizr), you may not need this line as it is included there as well.</p>
<h4 class="deeplink" id="fixing-ie9">Fixing IE9</h4>
<blockquote>
<p>While most versions of IE (even older ones!) are supported [by Picturefill] well, IE9 has a little conflict to work around. To support IE9, you will need to wrap a <code>video</code> element wrapper around the <code>source</code> elements in your <code>picture</code> tag. You can do this using conditional comments.
— <a href="http://scottjehl.github.io/picturefill/">Picturefill homepage</a></p>
</blockquote>
<p>As the documentation says, the polyfill provides support for <code><picture></code> across browsers, but IE9 requires that you wrap your <code>source</code> elements in a <code>video</code> tag. And since this fix is only required for IE9, you can place it in IE9-targeting conditional comments:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!--[if IE 9]><video style="display: none;"><![endif]--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!--[if IE 9]></video><![endif]--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<h3 class="deeplink" id="foreground-svg-images">Foreground SVG Images with Interactivity and Styleability</h3>
<p>As mentioned at the beginning of the article, the <code><img></code> element, and naturally the <code><picture></code> element, only allow you to load a static SVG image, or an SVG with animations defined internally. If you need to load a foreground image and you want that image to be interactive and styleable, you can use one of four available ways: <code><object></code>, <code><iframe></code>, <code><embed></code> and inline <code><svg></code>.</p>
<p>Both the <code><iframe></code> and <code><object></code> come with a default fallback mechanism.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>object</span> <span class="token attr-name">data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image.svg<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/svg+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- fallback here --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>object</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- fallback here --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></span></code></pre>
<p>An inline <code><svg></code> requires a different approach to provide fallbacks; one such approach uses the <code><foreignObject></code> element. You can read all about it <a href="http://www.kaizou.org/2009/03/inline-svg-fallback/">here</a>. Chris has also written about providing fallback for SVG <a href="http://css-tricks.com/svg-fallbacks/#the-fallbacks">here</a>.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>While using <code><picture></code> currently does require adding a JavaScript polyfill, using standard HTML5 markup and getting the flexibility of switching images using native elements is extremely powerful, and plugging the polyfill in is as easy as 1. download it, 2. add script to page, 3. you’re done. It’s absolutely worth it if you are doing art direction or providing fallback for multiple foreground SVG images.</p>
<p><code><picture></code> is more likely to become the standard way for art-directing SVG and providing <code>img</code> fallback in the future, so why start using it today? Both <code>img</code> way and the new <code>picture</code> require some JavaScript—for now, but the latter is definitely cleaner and more future-proof. Yes, <code>img</code> is also future-proof, but at some point, you get to ditch the polyfill and keep your code unchanged if you use <code>picture</code>, while <code>img</code> will either require you to keep using JavaScript <em>or</em> refactor your markup to make the switch to JavaScript-less <code>picture</code>.</p>
<p>Whether you decide to start using <code><picture></code> for SVG today or not, it is definitely worth getting to know better and using it for serving other responsive image formats. So here is a list of recommended articles to get you up and running:</p>
<h3 id="recommended-reading-on-%3Cpicture%3E" tabindex="-1">Recommended Reading on <code><picture></code></h3>
<ul>
<li><a href="http://www.html5rocks.com/en/tutorials/responsive/picture-element/">Introducing the <code><picture></code> element</a></li>
<li><a href="https://dev.opera.com/articles/native-responsive-images/">Native Responsive Images</a></li>
<li><a href="http://alistapart.com/article/responsive-images-in-practice">Responsive Images in Practice</a></li>
<li><a href="https://dev.opera.com/articles/responsive-images/">Responsive Images: Use Cases and Documented Code Snippets to Get You Started</a></li>
<li><a href="http://demosthenes.info/blog/936/Responsive-Images-For-Designers-The-HTML5-picture-element">Responsive Images For Designers: The HTML5 picture element</a></li>
</ul>
I Wrote A CSS Reference.
2015-02-04T00:00:00Z
https://sarasoueidan.com/blog/codrops-css-reference/
<p class="deck">Yesterday, we finally released a long-awaited project over at Codrops: <a href="http://tympanus.net/codrops/css_reference/">Codrops' new <strong>CSS Reference</strong></a>, authored by yours truly. Even though
I wrote <a href="https://sarasoueidan.com/blog/codrops-css-reference/">a post over at Codrops</a> introducing the reference and its features, I want to share a little bit more about my experience writing it.</p>
<p class="note update--neutral">
Please note that as of January 2016, I am no longer the maintainer of the Codrops CSS Reference.
</p>
<p>Thus, unlike my usual highly-technical blog posts, this is a (possibly-boring, but short!) post sharing a little more about the CSS Reference and how it came to be, and answering a couple of questions I got via Twitter since the release.</p>
<h3 class="deeplink" id="how-it-started">How It All Started</h3>
<p>I started writing for Codrops in 2013. Some time at the end of the year, I was thinking to myself: “Codrops would be an ultimate CSS reference if it had a CSS reference” (no pun intended). I thought that it would be fantastic if we had a CSS reference at hand for when someone needs to learn more about a specific CSS property used in one of the many creative demos found there. This idea came to my mind since I used to google for some of the properties I saw in the Codrops demos when I first started learning CSS and checking Codrops out regularly.</p>
<p>Around the same time that year, Manoela approached me with the same idea. It was an idea in my head, and part of a vision that Manoela and Pedro have for Codrops. It was only a few days later that I started digging into CSS specifications and writing the reference entries.</p>
<p>It took around 7–8 months to finish all of the entries. During that time, I had little time for side projects, but I did keep writing—albeit sporadically—on my blog and other blogs such as A List Apart, among others.</p>
<h3 class="deeplink" id="writing-the-entries">Writing The Reference Entries</h3>
<p>One of the main reasons I looked forward to starting the journey of writing the reference was knowing how much I will learn in the process. For me, that was enough of a reason to instantly say yes when Manoela approached me with the idea.</p>
<blockquote class="pull-quote">
One of the main reasons I looked forward to starting the journey of writing the reference was knowing how much I will learn in the process.
</blockquote>
<p>I knew it was a chance for me to know much more about CSS than I would otherwise know in such a short period of time. In order to write the description and information about a specific CSS feature, I had to dig deeper than I usually do into the specification where that CSS feature is defined.</p>
<p>As many of you may know, the specifications don’t <em>always</em> contain everything you need to know about a property, and the writing style is not always the clearest (which is one of the reasons we wrote the reference, too!), so I had to do a lot of research for many of the entries, reading great resources here and there, picking up a lot of knowledge and getting a lot of “ah-ha” moments in the process. There was <em>a lot</em> about CSS that I didn’t know, and that I <em>still</em> don’t know.</p>
<p>I wrote the CSS entries the same way and same style I usually write my articles. Now, some entries are exceptions because they don’t require a lot of elaboration. For example, properties like <code>border-color</code> are self-explanatory, so the tone of those entries is more “official” and less “me”.
<br />So, if you happen to enjoy my writing style, I can tell will enjoy reading the reference entries too.</p>
<p>Manoela and Pedro gave me a lot of freedom and flexibility to write the reference at my own pace and my own schedule and time, making sure I still had the time to work on other things as well so that writing the reference does not limit my creativity, my other client work, my blog, and, of course, my life!</p>
<h3 class="deeplink" id="releasing-the-reference">Releasing The Reference</h3>
<p>I can’t even begin to describe how excited I was to finally get to the point of releasing the reference! I usually get super nervous (read: almost get a heart attack) before publishing a blog post on my own low-profile blog, so publishing <strong>more than 300</strong> short to long articles on Codrops was extremely overwhelming and nerve-wrecking. I can always hear my heart beat when I tweet about a new blog post, and I certainly did when we finally got the word out about the reference.</p>
<p>It took us a little over a year to release the reference because of the amount of work each of us at Codrops had, besides the reference. 2014 was the year I started speaking at conferences, and in the second half of it (i.e. after having finished writing the entries) I got my hands full with conference work, writing, and other stuff that took my attention away from the reference for some time. But we finally made it.</p>
<h3 class="deeplink" id="questions">Questions</h3>
<p>I got a few of questions from followers and commenters, and two of these questions kept popping up.</p>
<h4 id="is-it-%E2%80%9Cthe-new-mdn%E2%80%9D%3F" tabindex="-1">Is it “the new MDN”?</h4>
<p>No, it’s not. At least it wasn’t our goal to make it so. It’s not even meant as competition to any other CSS Reference out there. It is simply another knowledge base added to what Codrops already offers on a regular basis.</p>
<p>I write about CSS (and SVG) a lot—be it on my blog, on Codrops, or one of the several other places I write at. The CSS Reference is, for me, simply an archive where I collected <strong>blog posts</strong> about a specific topic (CSS, in this case), and organized them in a way that is easier to browse and simpler for my readers to find what they are looking for.</p>
<blockquote class="pull-quote">
The CSS Reference is, for me, simply an archive where I collected <strong>blog posts</strong> about a specific topic (CSS, in this case), and organized them in a way that is easier to browse and simpler for my readers to find what they are looking for.
</blockquote>
<p>I sometimes even think of it as a book! Instead of writing a printed CSS book, I wrote an online book, that is open to your contributions and suggestions via a Github repo that we shared on Codrops. So it has an advantage over a regular book in that you, my dear readers, get to pinpoint any miskates, errors, ask for additions and suggestions, etc.</p>
<h4 id="why-didn%E2%80%99t-you-contribute-to-mdn-instead%3F" tabindex="-1">Why didn’t you contribute to MDN instead?</h4>
<p>I honestly don’t understand the reasoning behind this question. I mean, why would I <em>not</em> write the reference and choose to contribute to MDN <em>instead</em>?</p>
<p>That being said, I did mention a few reasons earlier why I chose to write the reference. Add to those that I love Codrops, and Manoela and Pedro gave me a lot of freedom and flexibility to give the reference its own character and style. They asked me to write it <em>because</em> they liked my writing style, and thus gave me the freedom to structure the entries the way I wanted. We did have some specifics—for example, the sections for the official syntax, browser support, etc. that we needed to have in every entry, and the rest was all up to me to write my own way.</p>
<p>Now, I don’t know if MDN would have allowed me to do the same, but Manoela knows how much I value the freedom and flexibility of work in my work, and she gave me just that, making the experience much more enjoyable, and feeling a lot less as a task or job.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>Writing the CSS reference has been a wonderful experience, and I am happy to have gotten the chance to do it. I know I haven’t literally written a book here (as in: a <em>printed</em> book), but I like to think that I have—maybe because it makes me feel a <em>liiiitle bit</em> less guilty for not having started writing my SVG book yet!</p>
<p>I wholeheartedly hope you like it, find it useful, and learn from it as much as I have learned from writing it. Your feedback on Twitter, in the comments and your contributions in the Github repo have been amazing—thank you so much for your support!</p>
<p>And thank you for reading this post, <em>and</em> reading the reference entries in the future.</p>
<p>–S</p>
Compositing And Blending In CSS
2015-01-27T00:00:00Z
https://sarasoueidan.com/blog/compositing-and-blending-in-css/
<p>If you’re a designer, then you’ve probably already come across blending effects some time or the other. Blending is one of the most frequently used effects in graphic and print design. You can add texture to text by blending it with its textured backdrop, create an illusion of <em>merged</em> images by blending these images together, and create a wide range of colorful effects that would not be possible without that fine level of color blending control.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/blending-examples.png" />
<figcaption>Examples of typographic and image effects created using CSS blend modes. See following sections for live demos.</figcaption>
</figure>
<p>Graphics editors such as Photoshop or Illustrator come with a set of blending <em>modes</em>. <strong>Blend modes</strong> allow you to specify <em>how</em> you want your elements to blend together, thus changing the colors of the area where these elements intersect. Each mode uses a specific color formula to mix the colors of the <em>source</em> and the <em>destination</em>.</p>
<p>Different modes give different end results. Before we talk about the different blend modes, and since we mentioned the <strong>source</strong> and <strong>destination</strong> elements, let’s have a quick look at the concept of compositing, and how it is related to blending in CSS.</p>
<h3 class="deeplink" id="compositing">What Is Compositing?</h3>
<p>Compositing is the combining of a graphic element with its <em>backdrop</em>.</p>
<p>A <strong>backdrop</strong> is <strong>the content behind the element</strong> and is what the element is composited with.</p>
<p><img src="https://sarasoueidan.com/assets/images/backdrop.png" alt="Backdrop Visual Illustration" /></p>
<p>Compositing defines how what you want to draw will be blended with what is already drawn on the canvas. The <strong>source</strong> is what you want to draw, and the <strong>destination</strong> is what is already drawn (the backdrop).</p>
<p>So, if you have two elements, and these elements overlap, you can think of the element on top as being the source, and the parts of the element behind that lie beneath it, will be the destination.</p>
<p>Using different composite operations (there are 16 operations), you can specify which parts of the two overlapping elements will be drawn, and which will not.</p>
<br />
<p><img src="https://sarasoueidan.com/assets/images/porter-duff.png" alt="Porter Duff Compositing Operations Visual" /></p>
<br />
<p>These composite operations are known as <strong>Porter Duff compositing operations</strong>. These operations specify what portions of the source and destination will be drawn, and blend modes specify how how the colors from the graphic element (source) and the backdrop (destination) interact. The illustrations in the above image are from the Compositing and Blending spec.
In HTML5 Canvas context, these oprations are specified using the <code>globalCompositeOperation</code> property, and can be used to clip backgrounds to specific shapes, such as text, thus creating the effect of texture-filled text in Canvas. I have written about this process in <a href="http://tympanus.net/codrops/2013/12/02/techniques-for-creating-textured-text/">this article</a> over at Codrops.</p>
<p>Together, Porter Duff Compositing and blending form the overall compositing operation for intersecting elements. According to the specification, “typically, the blending step is performed first, followed by the Porter-Duff compositing step. In the blending step, the resultant color from the mix of the element and the the backdrop is calculated. The graphic element’s color is replaced with this resultant color. The graphic element is then composited with the backdrop using the specified compositing operator.”</p>
<p>Therefore, the way two intersecting or overlapping elements are handled is by blending their colors based on a blend mode, and then drawing only the parts specified by the composite operation.</p>
<hr />
<p>In CSS, we have no way to specify a composite operation. The default composite operation used is <code>source-over</code>. Both the source and the destination elements remain, and the area where they intersect is blended using the blend mode specified.</p>
<hr />
<p>Before the <a href="http://www.w3.org/TR/compositing-1/">Compositing and Blending specification</a> was introduced, CSS allowed one type of composite operations: simple alpha compositing. This is what the <code>opacity</code> property is for. By changing an element’s opacity, the browser makes it translucent so that the colors of its backdrop can show through.</p>
<p>Today, two main properties exist that allow us to blend elements and backround images by specifying one of 16 available blend modes. These two properties are <code>background-blend-mode</code> and <code>mix-blend-mode</code>. Let’s get to know each.</p>
<h3 class="deeplink" id="background-blend-mode">Blending Background Layers: The <code>background-blend-mode</code> Property</h3>
<p>The <code>background-blend-mode</code> property, as its name suggests, is used to specify a blend mode for an element’s background layer. A background layer can be an image, or the background color.</p>
<p>In other words, <code>background-blend-mode</code> allows you to blend together an element’s background image with the images and/or background color behind it.</p>
<p>If the element has more than one background image, you can specify multiple blend modes—each blend mode will be used for a background image such that the first blend mode in the list corresponds to the first background image in the list of background images, and so on.</p>
<p>Then, each background layer is blended with the element’s background layer that is below it and the element’s background color. Which means that, if you have two background images and a background color:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>first-image.png<span class="token punctuation">)</span></span><span class="token punctuation">,</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>second-image.png<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token property">background-color</span><span class="token punctuation">:</span> orange<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token property">background-blend-mode</span><span class="token punctuation">:</span> screen<span class="token punctuation">,</span> multiply<span class="token punctuation">;</span></span></code></pre>
<p>The <code>second-image.png</code> background will blend with the background color using the <code>multiply</code> mode, and then the <code>first-image.png</code> background will blend with the second image and the background color using the <code>screen</code> blend mode. (Reminder: the first background image in the list is the one on top, and the ones following it are beneath it.)</p>
<p>Note that an element’s background layers must not blend with the content that is behind the element, instead they must act as if they are rendered into an <strong>isolated group</strong>.</p>
<p>Also note that if the <code>background</code> shorthand is used, the <code>background-blend-mode</code> property for that element must be reset to its initial value.</p>
<h3 class="deeplink" id="blend-modes">The Blend Modes</h3>
<p>Okay so we’ve established that each background layer can get its own blend mode which specifies how it blends with the layers beneath it. But what blend mode options do we have?</p>
<p><strong>There are 16 blend modes available in CSS</strong>: <code>normal</code> (which is the default blend mode and means that no blending is applied), <code>multiply</code>, <code>screen</code>, <code>overlay</code>, <code>darken</code>, <code>lighten</code>, <code>color-dodge</code>, <code>color-burn</code>, <code>hard-light</code>, <code>soft-light</code>, <code>difference</code>, <code>exclusion</code>, <code>hue</code>, <code>saturation</code>, <code>color</code> and <code>luminosity</code>.</p>
<p>Each filter, when applied to an image, for example, will give a different end result—the colors of the image are going to be changed based on the mode you choose.</p>
<dl>
<dt><code>normal</code></dt>
<dd>This is the default mode which specifies no blending. The blending formula simply selects the source color.
</dd>
<dt><code>multiply</code></dt>
<dd>The source color is multiplied by the destination color and replaces the destination. The resultant color is always at least as dark as either the source or destination color. <strong>Multiplying any color with black results in black. Multiplying any color with white preserves the original color.</strong>
</dd>
<dt><code>screen</code></dt>
<dd>Multiplies the complements of the backdrop and source color values, then complements the result. The result color is always at least as light as either of the two constituent colors. <strong>Screening any color with white produces white; screening with black leaves the original color unchanged.</strong> The effect is similar to projecting multiple photographic slides simultaneously onto a single screen.
</dd>
<dt><code>overlay</code></dt>
<dd>Multiplies or screens the colors, depending on the backdrop color value. Source colors overlay the backdrop while preserving its highlights and shadows. The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness of the backdrop.
</dd>
<dt><code>darken</code></dt>
<dd>Selects the darker of the backdrop and source colors. The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged.
</dd>
<dt><code>lighten</code></dt>
<dd>Selects the lighter of the backdrop and source colors. The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged.
</dd>
<dt><code>color-dodge</code></dt>
<dd>Brightens the backdrop color to reflect the source color. Painting with black produces no changes.
</dd>
<dt><code>color-burn</code></dt>
<dd>Darkens the backdrop color to reflect the source color. Painting with white produces no change.
</dd>
<dt><code>hard-light</code></dt>
<dd>Multiplies or screens the colors, depending on the source color value. The effect is similar to shining a harsh spotlight on the backdrop.
</dd>
<dt><code>soft-light</code></dt>
<dd>Darkens or lightens the colors, depending on the source color value. The effect is similar to shining a diffused spotlight on the backdrop.
</dd>
<dt><code>difference</code></dt>
<dd>Subtracts the darker of the two constituent colors from the lighter color. Painting with white inverts the backdrop color; painting with black produces no change.
</dd>
<dt><code>exclusion</code></dt>
<dd>Produces an effect similar to that of the Difference mode but lower in contrast. Painting with white <strong>inverts the backdrop color</strong>; painting with black produces no change.
</dd>
<dt><code>hue</code></dt>
<dd>Creates a color with the hue of the source color and the saturation and luminosity of the backdrop color.
</dd>
<dt><code>saturation</code></dt>
<dd>Creates a color with the saturation of the source color and the hue and luminosity of the backdrop color. Painting with this mode in an area of the backdrop that is a pure gray (no saturation) produces no change.
</dd>
<dt><code>color</code></dt>
<dd>Creates a color with the hue and saturation of the source color and the luminosity of the backdrop color. This preserves the gray levels of the backdrop and is useful for coloring monochrome images or tinting color images.
</dd>
<dt><code>luminosity</code></dt>
<dd>Creates a color with the luminosity of the source color and the hue and saturation of the backdrop color. This produces an inverse effect to that of the Color mode.
This mode is the one you can use to create monchrome "tinted" image effects like the ones you can see in different website headers.
</dd></dl>
<p>The following image shows the result of applying the different blend modes to an image, in the same order mentioned above, starting from the top left corner.</p>
<img src="https://sarasoueidan.com/assets/images/background-blend-mode-examples.png" alt="The result of applying 16 blend modes applied to an image." />
<p>For more information about these blend modes, I refer you to <a href="http://www.slrlounge.com/school/photoshop-blend-modes/">this article</a> on the SLR Lounge blog. It claims to be the ultimate visual guide to blend modes, and does include some nice explanations of the blend modes.</p>
<p>Personally, I think that even with the definition for each mode at hand, it can be really hard (if not impossible) to predict the result of applying these modes to an image.</p>
<p>Picking a suitable blend mode will be—in most cases—a case of trial and error. If you use Instagram you know that sometimes you just go over each of the available filters, applying them one after the other, till you get the effect you’re after. (I know I do that!)
With CSS blend modes, it’s practically the same.</p>
<p>For that reason, I’ve created a simple interactive demo that you can use to pick the right blend mode for your effects.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/css-blender-demo-screenshot.png" alt="Screenshot of the CSS Blender demo." />
<figcaption>Screenshot of the CSS Blender demo.</figcaption>
</figure>
<p>You can upload an image, and choose a background color to blend it with. The background color of the live preview (thumbnails) will live-update as you make your way around the color picker. <strong>Clicking on a thumbnail will preview the selected blend mode in the larger preview area.</strong></p>
<p><a href="http://sarasoueidan.com/demos/css-blender" class="button">Try the blend modes in the demo out.</a></p>
<br />
<p>Of course, the effects will be visible only in browsers that support the <code>background-blend-mode</code> property. <strong>For more information about browser support, refer to <a href="http://caniuse.com/#feat=css-backgroundblendmode">the compatibility table over at CanIUse.com</a>.</strong></p>
<hr />
<p>In addition to blending a background image with a background color in the interactive demo, you can also blend a piece of text with the element that has this background.</p>
<p>But blending separate elements together requires a property other than the <code>background-blend-mode</code> property. Let’s have a look at that next.</p>
<h3 class="deeplink" id="mix-blend-mode">Blending An Element With Elements In Its Backdrop: The <code>mix-blend-mode</code> Property</h3>
<p>As we mentioned earlier, a <strong>backdrop</strong> is <strong>the content behind the element</strong> and is what the element is composited with.</p>
<p>The content behind the element can be anything—including other elements. And this is where the interesting effects come in. Think fixed headers blending with the content as the page scrolls down, or text blended with an image in the background, or text blending with other text, etc.</p>
<p>Blending elements together is done using the <code>mix-blend-mode</code> property.</p>
<p>The <code>mix-blend-mode</code> property is similar to the <code>background-blend-mode</code> property, and takes the same blend mode values. So, you can specify any blend mode to get different blending effects.</p>
<p>For example, the text in the following image blends with the image behind it using <code>mix-blend-mode: difference</code>, giving the illusion of the water bubbles passing through and in front of the text. The reason this effect is established is the color inversion process of the <code>difference</code> mode.</p>
<img src="https://sarasoueidan.com/assets/images/mix-blend-mode-example-1.png" alt="Example of text blending with an image." />
<p>The area of the image where it overlaps with the text is the text’s backdrop, and that is where the blending happens. You can check the live demo out <a href="http://codepen.io/SaraSoueidan/pen/e90334f6ffdbb2a2cdd5604e769054e4?editors=110">here</a>.</p>
<p>Using <code>mix-blend-mode</code>, you can create many creative effects—far more than I could list in this post. One particularly interesting effect you can create is see-through text. Without CSS blend modes, you would need CSS masking and/or background clipping, along with some CSS hackery to create this effect and make it work.</p>
<p>With blend modes, and using the <code>difference</code> blend mode again, you can create this effect by—again—leveraging the color inversion process.</p>
<p>The following image shows this effect in action. It is merely a piece of text, positioned on top of an image, and blended with it.</p>
<p><img src="https://sarasoueidan.com/assets/images/see-through-text-mix-blend-mode.png" alt="Example of see-through text effect created using CSS blending." /></p>
<p>That’s pretty cool, isn’t it? You can check the live demo out <a href="http://codepen.io/SaraSoueidan/pen/887433527fac4e926e84b613be483bfc?editors=110">here</a>.</p>
<p>It is worth noting here that the colors you choose strongly affect the end result. That being said, the interactive demo can make picking the right colors for such an effect easier and faster.</p>
<p>In the interactive demo, you have an option to add editable text to the preview area, and then style that text and blend it with the preview image using <code>mix-blend-mode</code>. The following is a screenshot showing an example.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/css-blender-demo-screenshot-with-text.png" alt="Screenshot of the CSS Blender demo." />
<figcaption>Screenshot of the CSS Blender demo, with a piece of text blended into the preview image.</figcaption>
</figure>
<p><a href="http://sarasoueidan.com/demos/css-blender" class="button">Check the demo out.</a></p>
<hr />
<p>Since we’re talking about blending elements together, it only makes sense that we mention stacking contexts, especially considering that they affect how and what elements can be blended together.</p>
<p>According to the specification, applying a blend mode other than <code>normal</code> to the element must establish a new stacking context on that element, forming a <em>group</em>. This group must then be blended and composited with the stacking context that contains the element.</p>
<p>Also, an element that has blending applied, must blend with all the underlying content <strong><em>of the stacking context that that element belongs to</em></strong>. It will not blend with contents outside that context.</p>
<p>For example, the following image shows the result of mix-blending two images with the <code>overlay</code> mode. (<a href="http://codepen.io/SaraSoueidan/pen/09efabde7114d37a736525b5ab616bc5?editors=110">Live demo</a>)</p>
<img src="https://sarasoueidan.com/assets/images/mix-blend-mode-example-2.png" alt="Example of text blending with an image." />
<p>The code for the above simple example looks like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token entity named-entity" title="<">&lt;</span>div class="container"<span class="token entity named-entity" title=">">&gt;</span></span><br /><span class="highlight-line"> <span class="token entity named-entity" title="<">&lt;</span>img src="path/to/destination.png" alt="" /<span class="token entity named-entity" title=">">&gt;</span></span><br /><span class="highlight-line"> <span class="token entity named-entity" title="<">&lt;</span>div class="img-wrapper"<span class="token entity named-entity" title=">">&gt;</span></span><br /><span class="highlight-line"> <span class="token entity named-entity" title="<">&lt;</span>img src="path/to/source.png" alt="" class="source"/<span class="token entity named-entity" title=">">&gt;</span></span><br /><span class="highlight-line"> <span class="token entity named-entity" title="<">&lt;</span>/div<span class="token entity named-entity" title=">">&gt;</span></span><br /><span class="highlight-line"><span class="token entity named-entity" title="<">&lt;</span>/div<span class="token entity named-entity" title=">">&gt;</span></span></code></pre>
<p>I’ve wrapped the image on top (the <code>.source</code>) in a <code>div</code> that I’m going to create a stacking context on. Since the <code>opacity</code> property leads to the creation of a new stacking context when given a value other than th default (<code>1</code>), I’m going to use that.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.img-wrapper</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> .99<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>(Try it in the <a href="http://codepen.io/SaraSoueidan/pen/09efabde7114d37a736525b5ab616bc5?editors=110">live demo</a>.)</p>
<p>By creating a stacking context, the <code>.source</code> image no longer blends with the bottom image, because the latter lies outside the stacking context containing the former.</p>
<p>This is because we have <strong><em>isolated</em></strong> the image (and any other contents of the <code>.img-wrapper</code> context) from the rest of the elements, and thus they don’t blend with their backdrops anymore.</p>
<p>This brings us to the <code>isolation</code> property. But before we move on, note that the <code>mix-blend-mode</code> property also applies to SVG elements, and can be used to blend overlapping SVG elements together as well. As a matter of fact, the logo for the CSS Blender demo is a result of applying a <code>mix-blend-mode</code> to the three squares that make the demo up. You can see how the areas where these squares overlap have different colors due to the color blending applied.</p>
<p>Browser support for the <code>mix-blend-mode</code> property is not as wide as that of the <code>background-blend-mode</code> property. For details, refer to <a href="http://caniuse.com/#feat=css-mixblendmode">the browser compatibility table over at CanIUse.com</a>.</p>
<h3 class="deeplink" id="isolation">Isolating Elements: The <code>isolation</code> Property</h3>
<p>When a property that leads to the creation of a stacking context is applied to an element, that element is said to be <strong>isolated</strong>. The last example in the previous section is an example of this happening.</p>
<p>On the other hand, you can use the <code>isolation</code> property to isolate elements.</p>
<p>In SVG, this property defines whether an element is isolated or not. For CSS, setting <code>isolation</code> to <code>isolate</code> will turn the element into a stacking context, and thus affect whether or not the element’s contents can blend with their backdrop lying outside this context. By default, the <code>isolation</code> property is set to <code>auto</code>—which implies that they are not isolated.</p>
<p>If we were to go back to the previous example with the two blended images, we can prevent the image on top from blending with the image behind it by using the <code>isolation</code> property instead of the <code>opacity</code> property.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.img-wrapper</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">isolation</span><span class="token punctuation">:</span> isolate<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This has the same effect as using <code>opacity</code> in the previous example. If you use this rule instead of <code>opacity</code> in the live demo, you will get the same result.</p>
<p>Note that by creating a stacking context on an element, you are isolating the content of that element and preventing them from blending with the backdrop of that element. However, you can still apply a blend mode to the entire context to blend it with its backdrop.</p>
<p>Moreover, If you are using the<code>background-blend-mode</code> property, the <code>isolation</code> property is not needed since background layers must not blend with the content that is behind the element, instead they must act as if they are rendered into an isolated group (the element itself), as specified in the specification. This is why the <code>isolation</code> property will have an effect when used with the <code>mix-blend-mode</code> property, but not with the <code>background-blend-mode</code> property.</p>
<h3 class="deeplink" id="order-of-operations">Note: Order Of Graphical Operations</h3>
<p>CSS blending modes, <a href="http://www.w3.org/TR/filter-effects-1/">filters</a> and <a href="http://www.w3.org/TR/css-masking-1/">masks</a>, can all be applied to the same element. But which effect is applied first?</p>
<p>According to the specification, first any filter effect is applied, then any clipping, masking, blending and compositing.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>With all the graphical operations available to us via CSS, we are getting more possibilities for designing in the browsers—this is particularly interesting if you—like me—are not into graphics editors and don’t know your way around them well.</p>
<p>The web platform team at Adobe have been doing a tremendous job bringing many of their tools’ graphical capabilities to the web. From filters, to blend modes, clipping and masking, and even <a href="http://sarasoueidan.com/blog/css-shapes">CSS Shapes</a>, we are gaining more control over layout and graphics on the web.</p>
<p>Many creative effects can be created using CSS blend modes, and when combined with other technologies, they open a door to endless creative possibilities.</p>
<hr />
<p>I hope you liked this article and found it useful.</p>
<p>Thank you for reading!</p>
Useful SVGO[ptimization] Tools
2015-01-26T00:00:00Z
https://sarasoueidan.com/blog/svgo-tools/
<p class="deck">One of the steps you need to do when working with SVG is optimizing the SVG code after exporting it from the editor and before embedding in on your web page. For that, several standalone optimization tools exits. The two tools I usually mention in my <a href="http://www.smashingmagazine.com/2014/11/03/styling-and-animating-svgs-with-css/">articles</a> and <a href="http://slides.com/sarasoueidan/working-with-svg-a-primer#/">talks</a> are <a href="http://petercollingridge.appspot.com/svg-editor">Peter Collingridge's online editor</a>, and <a href="https://github.com/svg/svgo">SVGO</a>. In this article, I'm going to introduce you to a new SVGO Tool that provides us with everything Peter's tool does, and a bit more.</p>
<p>This is not to say that peter’s tool is no longer useful—it certainly is. But if you use SVGO, then you know how convenient it is with all the available tools it comes with.</p>
<h3 class="deeplink" id="what-is-svgo">What is SVGO?</h3>
<p>For those of you who are not familiar with SVGO: it is a node-js based SVG optimization tool. (SVGO is an abbreviation for <strong>SVG Optimization</strong>). It comes with a set of tools and plugins that make it a great tool that you can integrate into almost any kind of workflow. (We’ll go over these tools shortly.)</p>
<p>However, SVGO has one disadvantage: it can easily break your SVG—especially if it is animated or scripted, the document structure will change and eventually break any animations or scripting applied.</p>
<p>Even with the many SVGO tools and plugins available, unfortunately, we had no way to preview the result of applying SVGO optimizations to an SVG, to tell whether they will break it or not… until now.</p>
<h3 class="deeplink" id="introducing-svgomg">Introducing SVGOMG</h3>
<p>Last month, when I wrote the <a href="http://calendar.perfplanet.com/2014/tips-for-optimising-svg-delivery-for-the-web/">SVG performance article</a> for Perf Calendar, Google’s <a href="http://twitter.com/jaffathecake">Jake Archibald</a> tech-reviewed the article. And while discussing the code optimization section, I mentioned how SVGO lacks a GUI that allows us to preview the result of running the optimizations on our SVGs, and how it should be used with caution because of that.</p>
<p>A week or two later, Jake made the <a href="http://jakearchibald.github.io/svgomg/">SVGOMG GUI</a>—an interface that allows you to optimize SVGs by selecting your optimizations, and getting a live preview of how your SVG looks with these optimizations applied.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svgomg.png" alt="Screenshot of the SVGOMG GUI." />
<figcaption>Screenshot of the SVGOMG GUI.</figcaption>
</figure>
<p>If you’ve used Peter’s tool before, you can expect the same from SVGOMG, and <em>more</em>.</p>
<p>You can upload an SVG file, paste SVG code in, or load the default demo SVG—if you’re just trying the app out. A set of options will then be revealed on the right side of the screen (see screenshot above). These options represent the optimizations built into SVGO. The SVGO optimizations are plugins that you can enable and disable as needed, and SVGOMG offers you a visual way of doing so.</p>
<p>The live preview section will update as you choose your optimizations, allowing you to detect and disable any optimizations that would break your SVG.</p>
<p>In the top right corner, you can see the current file size and the optimization percentage. You also have an option to show the original SVG—which will also display the original file size, to compare your it to your optimized version.</p>
<p>SVGOMG is an online tool. But if you know Jake then you’ll probably expect it to also work offline, considering that he might just be the biggest proponent of ServiceWorker and offline-first out there. He has written about it a lot <a href="http://jakearchibald.com/">on his blog</a>. Thus—expectedly—<strong>the GUI works offline too</strong> in any browser that’s got ServiceWorker. (For an overview of the state of ServiceWorker, refer to <a href="https://jakearchibald.github.io/isserviceworkerready/">this browser compatibility page</a>.) Once you’ve opened the app (try it in Chrome, for example), you will get a notification a couple of seconds later telling you that the GUI is ready to work offline.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/svgomg-offline.png" alt="Screenshot of SVGOMG showing the notification that the GUI works offline." />
<figcaption>Screenshot of SVGOMG showing the notification that the GUI works offline.</figcaption>
</figure>
<p><em>Very</em> useful, isn’t it?</p>
<h3 class="deeplink" id="other-svgo-tools">Other SVGO Tools</h3>
<p>There are quite a few SVGO plugins available that can be used in a variety of ways, depending on your preferred workflow:</p>
<h4 class="deeplink" id="adobe-illustrator-plugin">Adobe Illustrator Plugin</h4>
<p>A GUI allowing you to select the optimizations you want to apply also exists in another SVGO tool called <a href="https://github.com/davidderaedt/SVG-NOW"><strong>SVG NOW</strong></a>.</p>
<img src="https://sarasoueidan.com/assets/images/svgnow.png" alt="SVG NOW plugin logo." />
<p>SVG NOW is an Illustrator plugin that brings SVGO’s optimizations into Illustrator. It is an alternative SVG exporter for AI, aimed at optimizing SVG files by post-processing the generated SVG code using SVGO. This is useful for sure, <strong>but SVG NOW does not show you a live preview of how the SVG is affected by the optimizations you choose</strong>.</p>
<h4 class="deeplink" id="inkscape-plugin">Inkscape Plugin</h4>
<p>Similar to SVG NOW, <a href="https://github.com/juanfran/svgo-inkscape"><strong>SVGO-Inkscape</strong></a> is an Inkscape plugin that allows you to remove a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff generated by Inkscape and that can be safely removed or converted without affecting SVG rendering result.</p>
<h4 class="deeplink" id="sketch-plugin">Sketch Plugin</h4>
<p><a href="https://github.com/BohemianCoding/svgo-compressor">SVGO Compressor</a> is the Sketch version of the SVGO plugin, which compresses SVG assets using SVGO, right when you export them. As with other SVGO tools, there’s no way of telling how the exported SVG will be affected by the optimizations applied.</p>
<p>From my personal experience I’ve found that SVGs exported using Sketch were more likely to break when optimized with SVGO, because of how Sketch exports certain SVG shapes using clip paths and masks. So it’s useful to remember that you might need to re-export and re-optimize the SVG another way if and when this happens.</p>
<p>Note that the plugin <em>requires</em> Sketch 3.8, and so it won’t work with older versions of the application.</p>
<h4 class="deeplink" id="drag-n-drop-gui">Drag'n'Drop GUI</h4>
<p>Another kind of GUI for SVGO exists — called <a href="https://github.com/svg/svgo-gui"><strong>SVGO GUI</strong></a> — that allows you to drag-and-drop your SVGs and then optimizes those SVG on-the-fly, replacing your original ones with the optimized versions.</p>
<img src="https://sarasoueidan.com/assets/images/svgogui.png" alt="Screenshot of the SVGO GUI." />
<p>This GUI is useful for quick bulk optimizations, but it risky considering that 1) you cannot preview the optimized SVG 2) your original SVGs are instantly replaced by the optimized versions, which means that your SVGs might break, and the broken ones will replace the original ones. If you’re going to use this GUI, make sure you have a backup before you optmize.</p>
<h4 class="deeplink" id="grunt-and-gulp-plugins">Grunt and Gulp Plugins</h4>
<p>SVGO is probably mostly known for its <a href="https://github.com/sindresorhus/grunt-svgmin"><strong>Grunt plugin</strong></a>, and its sister the <a href="https://github.com/ben-eb/gulp-svgmin"><strong>Gulp plugin</strong></a>. Both of these plugins allow you to enable and disable SVGO optimizations as needed.
For example, a default SVGO optimization will remove the <code>viewBox</code> attribute from your SVG. This is something you should completely avoid, because, without the <code>viewBox</code> attribute, the SVG cannot be <a href="http://tympanus.net/codrops/2014/08/19/making-svgs-responsive-with-css/">made responsive</a>. (For more information about the <code>viewBox</code> attribute, refer to my tutorial <a href="http://sarasoueidan.com/blog/svg-coordinate-systems">here</a>.)</p>
<p>When you set up SVGO in Grunt, for example, you can disable any plugins you want. For example, to disable removing the <code>viewBox</code>, add <code>removeViewBox: false</code> to the <code>Plugins</code> object in your configuration file.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">// Source: https://github.com/sindresorhus/grunt-svgmin</span></span><br /><span class="highlight-line">grunt<span class="token punctuation">.</span><span class="token function">initConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">svgmin</span><span class="token operator">:</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">options</span><span class="token operator">:</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">removeViewBox</span><span class="token operator">:</span> <span class="token boolean">false</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">removeUselessStrokeAndFill</span><span class="token operator">:</span> <span class="token boolean">false</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">]</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token literal-property property">dist</span><span class="token operator">:</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token literal-property property">files</span><span class="token operator">:</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string-property property">'dist/unicorn.svg'</span><span class="token operator">:</span> <span class="token string">'app/unicorn.svg'</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>You can refer to the plugin’s Github repo for more information.</p>
<h4 class="deeplink" id="osx-folder-action">OS X Folder Action</h4>
<p>SVGO also comes with an <a href="https://github.com/svg/svgo-osx-folder-action"><strong>OS X folder action</strong></a> that you can attach to any folder, and then have your SVGs optimized on-the-fly as soon as you place them in that folder. Again, optmized versions will replace the original ones, so be careful.</p>
<h2 id="final-words" tabindex="-1">Final Words</h2>
<p>No matter what your workflow is, SVGO can probably fit into it, one way or another.</p>
<p>I’m sure other SVG optimization tools exits, but with all the options SVGO comes with, if you ask me, this is the tool I would recommend.</p>
<p>Optimize those SVGs, build something beautiful, and share it with the world.</p>
<p>Thank you for reading.</p>
Using The CSS :target Selector To Create JavaScript-less UI Effects
2015-01-24T00:00:00Z
https://sarasoueidan.com/blog/css-target-uses/
<p class="deck">
You may or may not have used the :target selector before; and you may or may not have used it to show and hide elements without having to resort to JavaScript to handle this event for you. This article I wrote for the Adobe Dreamweaver team blog, serves as a short introduction to the :target selector, showing how you can use it to create JavaScript-less UI effects—for example, overlays, modals, etc.
</p>
Tips For Optimizing SVG Delivery For The Web
2014-12-19T00:00:00Z
https://sarasoueidan.com/blog/svg-optimization/
<p class="deck">
SVGs are a great asset in our responsive web design toolkit. But just like any other image format, there are certain steps you should take to make sure you’re delivering optimised resources that don’t have a negative impact on your page’s performance. Here are some things that you can do to make sure you’re delivering better SVGs for the web.
</p>
An Overview Of SVG Sprite Creation Techniques
2014-12-16T00:00:00Z
https://sarasoueidan.com/blog/overview-of-svg-sprite-creation-techniques/
<p class="deck">
SVG can be used as an icon system to replace icon fonts, and there are several ways to create SVG sprites. This article I wrote for this year's 24Ways will give you an overview of three of them. While we’re at it, we’re going to take a look at some of the available tools used to automate sprite creation and fallback for us.
</p>
Styling and Animating SVGs with CSS
2014-11-03T00:00:00Z
https://sarasoueidan.com/blog/styling-and-animating-svgs-with-css/
<p class="deck">
CSS can be used to style and animate scalable vector graphics, much like it is used to style and animate HTML elements. In this article I wrote for Smashing Magazine, which is a modified transcript of a talk I recently gave at CSSconf EU and From the Front, I’ll go over the prerequisites and techniques for working with CSS in SVG.
</p>
A Guide to SVG Animations (SMIL)
2014-10-13T00:00:00Z
https://sarasoueidan.com/blog/guide-svg-animations-smil/
<p class="deck">
What the title says: a complete guide to SVG animations derived from the SMIl specification. The extensive guide features a lot of demos and goes over the animations syntax, covering almost everything you need to know to get started with SVG Animations today.
</p>
Making SVGs Responsive With CSS
2014-08-19T00:00:00Z
https://sarasoueidan.com/blog/responsive-svgs/
<p class="deck">
An article on how to make embedded SVGs cross-browser responsive. We're going to cover embedding techniques, how to apply the "Padding Hack" and how to use inline media queries to make SVGs adaptive.
</p>
Understanding SVG Coordinate Systems and Transformations (Part 3) — Establishing New Viewports
2014-08-05T00:00:00Z
https://sarasoueidan.com/blog/nesting-svgs/
<p>At any point in an SVG drawing, you can establish new viewports and user coordinate systems by either nesting <code>svg</code>s or using elements such as the <code>symbol</code> element, among others. In this article we’re going to have a look at how we can do that and how this can be useful for controlling SVG elements and making them more flexible (and/or fluid).</p>
<p>This is the third and last article in a series of articles about SVG coordinate systems and transformations. In the first article, I covered everything you need to know to understand the basics of SVG coordinate systems; more specifically, the SVG viewport, and the <code>viewBox</code> and <code>preserveAspectRatio</code> attributes. In the second article, you can find everything you need to know about how SVG system transformations work.</p>
<ul>
<li><a href="http://sarasoueidan.com/blog/svg-coordinate-systems">Understanding SVG Coordinate Systems & Transformations (Part 1) – The viewport, <code>viewBox</code>, and <code>preserveAspectRatio</code></a></li>
<li><a href="http://sarasoueidan.com/blog/svg-transformations">Understanding SVG Coordinate Systems & Transformations (Part 2) – The <code>transform</code> Attribute</a></li>
<li>Understanding SVG Coordinate Systems & Transformations (Part 3) – Establishing New Viewports</li>
</ul>
<p>Throughout this article, <strong>I’m going to assume that you read the <u>first</u> part of this series about SVG viewports and the <code>viewBox</code> and <code>preserveAspectRatio</code> attributes</strong>. You don’t need to have read the second one about coordinate system transformations to follow along this article.</p>
<h3 class="deeplink" id="nesting-svgs"> Nesting svg Elements</h3>
<p>In the <a href="https://sarasoueidan.com/blog/svg-coordinate-systems">first part</a> we talked about how the <code><svg></code> element establishes a viewport for the content of the SVG canvas. At any point in an SVG drawing, you can establish a new viewport into which all contained graphics is drawn by including an <code><svg></code> element inside another <code><svg></code>. By establishing a new viewport, you also implicitly establish a new viewport coordinate system and a new user coordinate system.</p>
<p>For example, suppose you have an <code><svg></code> and some content inside it:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- some SVG content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- some inner SVG content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The first thing to note here is that the inner <code><svg></code> element does not require specifying a namespace <code>xmlns</code> on it because it is assumed to be the same namespace as the outer <code><svg></code>'s namespace. Of course, even the outer <code><svg></code> does not require a namespace if it is embedded inline in an HTML5 document.</p>
<p>You can use a nested SVG to group elements together and then position them inside the parent SVG. Now, you can also group elements together and position them using the <code><g></code> group—by wrapping elements inside a <a href="https://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg">group <code><g></code> element</a>, you can position them on the canvas by <a href="https://sarasoueidan.com/blog/svg-transformations">using the <code>transform</code> attribute</a>. However, an <code><svg></code> element has certain advantages over using <code><g></code>. Positioning using <code>x</code> and <code>y</code> coordinates is, in most cases, more convenient than using transforms. Moreover, an <code><svg></code> element accepts <code>width</code> and <code>height</code> attributes, which the <code><g></code> element doesn’t. That said, the <code><svg></code> may not always be needed or necessary, because it leads to the creation of a new viewport and coordinate systems, which you may not need or want.</p>
<p>By specifying a width and height to the <code><svg></code>, you restrict the content inside it to the bounds of the viewport that is defined by the <code>x</code>, <code>y</code>, <code>width</code>, and <code>height</code> attributes. Any content that lies beyond these bounds will be clipped.</p>
<p>If you don’t specify <code>x</code> and <code>y</code> attributes, they’re assumed to be zero. If you don’t specify <code>height</code> and <code>width</code> attributes, the <code><svg></code> will be 100% the width and height of its parent SVG.</p>
<p>Moreover, specifying a user coordinate system other than the default one will also affect the content inside the inner <code><svg></code>, too.</p>
<p>Percentage values specified for elements inside the inner <code><svg></code> will be calculated relative to it, not relative to the outer <code>svg</code>. Percentage values specified on the inner itself <code><svg></code> will be calculated relative to the outer <code>svg</code>. For example, the following will result in the inner SVG being equal to 400 units, and the rectangle inside it will be 200 units:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span> <span class="token attr-name">..</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50%<span class="token punctuation">"</span></span> <span class="token attr-name">...</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>If the width of the outermost <code><svg></code> is set to 100% (for example, if it is embedded inline in a document and you want it to be fluid), then the inner SVG will expand and shrink as necessary to maintain a width that is half of that of the outer SVG – this is powerful.</p>
<p>Nested SVGs are particularly useful for adding flexibility and fluidness to elements on the SVG canvas. We know that, using <code>viewBox</code> values and <code>preserveAspectRatio</code>, we can already create responsive SVGs. The outermost <code><svg></code>'s with can be set to 100% to make sure it expands and shrinks as its container (or the page) grows or shrinks. Then, by using <code>viewBox</code> values and <code>preserveAspectRatio</code>, we can make sure that the SVG canvas also responds to the changes in the viewport (outermost <code>svg</code>). I’ve written about responsifying SVGs in my <a href="https://docs.google.com/presentation/d/1Iuvf3saPCJepVJBDNNDSmSsA0_rwtRYehSmmSSLYFVQ/pub?start=false&loop=false&delayms=3000#slide=id.p">CSSConf talk slides</a>. You can check the technique out <a href="https://docs.google.com/presentation/d/1Iuvf3saPCJepVJBDNNDSmSsA0_rwtRYehSmmSSLYFVQ/pub?start=false&loop=false&delayms=3000#slide=id.g180585666_036">here</a>.</p>
<p>However, when we do responsify an SVG like that, the entire canvas with <em>all</em> the elements drawn on it will respond and change simultaneously. But sometimes, you may want to have only one element inside the graphic to be flexible, while keeping others “fixed” in position and/or size. This is where nested <code>svg</code>s can be useful.</p>
<p>An <code>svg</code> element can have its own coordinate system independent of its parent, and it can have its own <code>viewBox</code> and <code>preserveAspectRatio</code> attributes that allow you to size and position the content inside it any way you want.</p>
<p>So, in order to make one element flexible, we can wrap it in an <code><svg></code> element, and give that <code>svg</code> a flexible width so that it adjusts to the width of the outermost SVG, and then specify <code>preserveAspectRatio="none"</code> so that the graphic inside it also stretches and shrinks with the container width. (Note that <code>svg</code>s can be nested to many levels, but in order to keep things simple, I’m nesting only one level deep in this article.)</p>
<p>To demonstrate how nested <code>svg</code>s can be useful, let’s look at some examples.</p>
<h4 class="deeplink" id="example">Example</h4>
<p>Suppose we have the following SVG:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1.png" alt="" />
</figure>
<p>The above SVG is responsive. Resizing the screen will result in the entire SVG graphic responding as necessary. The following screenshot shows the result of shrinking the page, and how the SVG becomes smaller. Notice how <strong>the contents of the SVG maintain all their initial positions with respect to the SVG viewport and with respect to each other</strong>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-resized.png" alt="" />
</figure>
<p>Using nested SVGs, we’re going to change that. We can specify a position for individual elements inside the SVG relative to the SVG’s viewport, so that as the SVG viewport size changes (i.e the size of the outermost <code>svg</code> changes), the elements respond independently of each other.</p>
<p class="note">
Note that, at this point, it is necessary that you be familiar with how the SVG viewport, `viewBox`, and `preserveAspectRatio` work.
</p>
<p>We’re going to create an effect such that, when the screen is resized, the upper part of the egg is going to be moved so that the cute chicken inside it peeks out, as shown in the following image:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-new.png" alt="" />
</figure>
<p>In order to get that effect, the egg’s upper part has to be separated from the rest by wrapping it inside an <code>svg</code> of its own. This <code>svg</code> wrapper will get an ID <code>upper-shell</code>.</p>
<p>Then, we’re going to make sure the new <code>svg#upper-shell</code> has the same height and width as the outer SVG. This can be achieved by either specifying <code>width="100%" height="100%"</code> on the <code>svg</code>, or by not specifying any height and width at all. If no width and height are specified on the inner SVG, it automatically expands to 100% the width and height of the outer SVG.</p>
<blockquote class="pull-quote">
If no width and height are specified on the inner SVG, it automatically expands to 100% the width and height of the outer SVG.
</blockquote>
<p>And finally, to make sure the upper shell is “lifted” up or positioned at the top center of the <code>svg#upper-shell</code>, we’re going to use the appropriate <code>preserveAspectRatio</code> value which makes sure the viewBox is positioned at the top center of the viewport—the value is <code>xMidYMin</code>.</p>
<p>The code for the SVG graphic becomes:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.1<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 315 385<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMid meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- the chicken illustration --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chicken<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- path forming the lower shell --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lower-shell<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#gradient)<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.5003<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>upper-shell<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 315 385<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMin meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- path forming the upper shell --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>the-upper-shell<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#gradient)<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.5003<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p class="note">I've stripped out the parts that relevant to the article like the gradient used to color the egg shells and the paths forming the shapes, just for the sake of brevity in the example code.</p>
<p>At this point, note that the <code>viewBox</code> specified on the nested <code>svg#upper-shell</code> has the same value as that of the outermost <code>svg</code> (before it was removed). The reason we used the same <code>viewBox</code> value is so that, the SVG maintains its original look on big screens.</p>
<p>So, the way this goes is: we start with an SVG—in our case, it’s the image of a cracked egg with a chicken hidden inside it.
Then, we create another “layer” and promote the upper shell to it—this layer is created by using a nested <code>svg</code>.
The nested <code>svg</code> has the same dimensions as the outer <code>svg</code> and the same <code>viewBox</code>.
And finally, the viewBox of the inner SVG is set to “stick” to the top of the viewport no matter what the screen size is—this makes sure that, when the screen size is narrow and the SVG is elongated, the upper shell will be lifted upwards, thus showing the chicken “behind” it on the canvas.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-layered.png" alt="" />
</figure>
<p>Once the screen size shrinks, the SVG is elongated, and the viewBox containing the upper shell is positioned at the top of the viewport using <code>preserveAspectratio="xMidYMin meet"</code>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-viewbox.png" alt="" />
<figcaption>
The translucent purple layer shows the viewport established by the nested <code>svg</code>. The translucent orange box shows the <code>viewBox</code> inside the <code>svg</code>—it is positioned at the top center of the viewport using <code>preserveAspectratio="xMidYMin meet"</code>.
</figcaption>
</figure>
<p>Click on the following button to see the live SVG. Remember to resize your browser to see the SVG adapt.</p>
<p><a href="https://sarasoueidan.com/assets/images/svg-nesting-chick.svg" class="button">View Live Example</a></p>
<p>Nesting or “layering” SVGs allows you to position parts of the SVG relative to the changing viewport, while maintaining the elements’ aspect ratio. So the image adapts without distorting the elements inside it.</p>
<p>If we wanted the entire egg to come off the chicken, we could always wrap the lower shell in an <code>svg</code> layer of its own, having the same <code>viewBox</code>, too. Then, to make sure the lower shell moves down and sticks to the bottom center of the viewport, we position it using <code>preserveAspectRatio="xMidYMax meet"</code>. The code would look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.1<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chick<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 315 385<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMid meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- the chicken illustration --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chick<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>upper-shell<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 315 385<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMid meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- path forming the upper shell --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>the-upper-shell<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#gradient)<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.5003<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lower-shell<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 315 385<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xMidYMax meet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- path forming the lower shell --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>the-lower-shell<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#gradient)<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.5003<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Each of the <code>svg</code> layers/viewports is equal to 100% the width and height of the outermost <code>svg</code>. So we basically have three clones. Each layer contains an element—the upper shell, the lower shell, or the chick. The <code>viewBox</code> for the three layers is the same, with only the <code>preserveAspectRatio</code> being different.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-nesting-example-1-2.png" alt="" />
<figcaption>
The three <code>svg</code>s have a 100% height and width value covering the entire outermost viewport. Their <code>viewBox</code>es are also equal as you can see in the image. Only the position of these <code>viewBox</code>es is different (specified using <code>preserveAspectRatio</code>).
</figcaption>
</figure>
<p>Of course, in this example I started with a graphic of a chicken hiding inside an egg, and that is revealed when the screen becomes smaller. However, you could do something different: you could start with a graphic on a small screen, that then reveals something new as the screen becomes bigger; i.e as the <code>svg</code> becomes wider and there is more horizontal space to show elements.</p>
<p>You could get a lot more creative, and show and hide elements according to different screen sizes—using media queries—and have the new elements be positioned in a certain way to achieve a certain effect. The sky is the limit here.</p>
<p>Also note that the nested <code>svg</code>s don’t need to have the same height and width as their containing <code>svg</code>; you can specify a height and width and have the content of the <code>svg</code> be limited to and clipped by the boundaries specified by that height and width—it all boils down to what you’re trying to achieve.</p>
<h4 class="deeplink" id="making-elements-fluid">Making Elements Fluid Using Nested SVGs</h4>
<p>In addition to positioning elements while preserving their aspect ratios, we can use nested <code>svg</code>s allow only certain elements to be fluid—this can be done by <em>not</em> preserving the aspect ratio of these particular elements.</p>
<p>For example, if you want only one element in the SVG to be fluid, you can wrap it in an <code>svg</code>, and use <code>preserveAspectRatio="none"</code> to have that element expand to fill the entire width of the viewport at all times, while maintaining the aspect ratio and positioning of other elements like we did in the previous example.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- this content will be fluid --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>..<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- content positioned somewhere in the viewport --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="http://jakearchibald.com/">Jake Archibald</a> created a simple and practical use case for nested SVGs that does exactly that: a simple UI that contains elements positioned at the corners of the outermost <code>svg</code>, maintaining their aspect ratios, and a middle part of the UI is fluid and responds to the change in the <code>svg</code> width by shrinking and expanding with it. You can check his demo out <a href="https://jsbin.com/loceqo/1">here</a>. Make sure you inspect the code in the dev tools to select and visualize the different <code>viewbox</code>es and <code>svg</code>s used.</p>
<h3 class="deeplink" id="establishing-viewports">Other ways to establish new viewports</h3>
<p><code>svg</code> elements are not the only elements that establish new viewports in SVG. In the following sections, we’re going to go over the other ways to establish new viewports using other SVG elements.</p>
<h4 class="deeplink" id="using-symbol">Establishing a new viewport by `use`ing `symbol` </h4>
<p>The <code>symbol</code> element defines a new viewport whenever it is instantiated by the <code>use</code> element.</p>
<p>A <code>symbol</code> element can be <code>use</code>d by referencing it in the <code>xlink:href</code> attribute of the <code>use</code> element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-symbol<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 300 200<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- contents of the symbol --></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- this content is only rendered when `use`d --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>symbol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#my-symbol<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The question marks used as values above are used only to indicate that these values may or may not be specified—if <code>x</code> and <code>y</code> are not specified, they default to zero, and you don’t need to specify a height and width either.</p>
<p>You see, when you <code>use</code> a <code>symbol</code> element, and then inspect the DOM using the dev tools, you will not see the contents of the <code>symbol</code> inside the <code>use</code> tag. The reason for this is that the contents of <code>use</code> are rendered into a shadow tree, which you can inspect if you enable inspecting the shadow DOM in the dev tools.</p>
<p>When the <code>symbol</code> is used, it is deeply cloned into the generated shadow tree, with the exception that the <code>symbol</code> is replaced by an <code>svg</code>. This generated <code>svg</code> will always have explicit values for attributes <code>width</code> and <code>height</code>. If attributes <code>width</code> and/or <code>height</code> are provided on the <code>use</code> element, then these attributes will be transferred to the generated <code>svg</code>. If attributes <code>width</code> and/or <code>height</code> are not specified, the generated <code>svg</code> element will use values of 100% for these attributes.</p>
<blockquote class="pull-quote">
When the <code>symbol</code> is used, it is deeply cloned into the generated shadow tree, with the exception that the <code>symbol</code> is replaced by an <code>svg</code>.
</blockquote>
<p>Because we end up with an <code>svg</code> in the DOM, and because this <code>svg</code> is practically contained in the outer <code>svg</code> where <code>use</code> is used, we’re left with a nested <code>svg</code> situation not very different from the one we talked about in the previous section—the nested <code>svg</code> forms a new viewport. The <code>viewBox</code> for the nested <code>svg</code> is the <code>viewBox</code> specified on the <code>symbol</code> element. (The <code>symbol</code> element accepts a <code>viewBox</code> attribute value. For more information, refer to the article: <a href="https://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg/">Structuring, Grouping, and Referencing in SVG – The <code><g></code>, <code><use></code>, <code><defs></code> and <code><symbol></code> Elements</a>).</p>
<p>So we now have a new viewport whose dimensions and position can be specified in the <code>use</code> element (<code>x</code>, <code>y</code>, <code>width</code>, <code>height</code>), and whose <code>viewBox</code> value can also be specified in the <code>symbol</code> element. The content of the <code>symbol</code> is then rendered and positioned inside this viewport and viewBox.</p>
<p>And last but not least, the <code>symbol</code> element also accepts a <code>preserveAspectratio</code> attribute value, that allows you to position the <code>viewBox</code> inside the new viewport established by <code>use</code>. Pretty neat, right? You can control the newly created nested <code>svg</code> just like we did in the previous sections.</p>
<p><a href="http://eleqtriq.com/">Dirk Weber</a> has also created a demo that uses nested SVGs and <code>symbol</code> elements to mimic the behavior of CSS border images. You can check his article out <a href="http://w3.eleqtriq.com/2014/02/the-4-slice-scaling-technique-for-svg/">here</a>.</p>
<h4 class="deeplink" id="referencing-svg-using-image">Establishing a new viewport by referencing an SVG image in `image`</h4>
<p>The <code>image</code> element indicates that the contents of a complete file are to be rendered into a given rectangle within the current user coordinate system. The <code>image</code> element can refer to raster image files such as PNG or JPEG or to files with MIME type of “image/svg+xml”.</p>
<p>An <code>image</code> element that references an SVG file will result in the establishment of a temporary new viewport since the referenced resource by definition will have an <code>svg</code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myGraphic.svg<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token attr-name">preserveAspectRatio</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>?<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The <code><image></code> element accepts many attributes, some of these attributes—the ones that are relevant to this article—are <code>x</code> and <code>y</code> position attributes, <code>width</code> and <code>height</code> attributes, and <code>preserveAspectratio</code>.</p>
<p>Normally, an SVG file will contain a root <code><svg></code> element; this element may or may not have position and dimensions specified, in addition to a <code>viewBox</code> and a <code>preserveAspectratio</code> value.</p>
<p>When an <code>image</code> element references an SVG image file, the <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code> attributes on the root <code>svg</code> are ignored. Unless the value of <code>preserveAspectRatio</code> on the <code>image</code> element starts with ‘defer’, the <code>preserveAspectRatio</code> attribute on the root element in the referenced SVG image is also ignored. Instead, the <code>preserveAspectRatio</code> attribute on the referencing <code>image</code> element defines how the SVG image content is fitted into the viewport.</p>
<p>The value of the <code>viewBox</code> attribute to use when evaluating the <code>preserveAspectRatio</code> attribute is defined by the referenced content. For content that clearly identifies a viewBox (e.g. an SVG file with the <code>viewBox</code> attribute on the outermost svg element) that value should be used. For most raster content (PNG, JPEG) the bounds of the image should be used (i.e. the <code>image</code> element has an implicit <code>viewBox</code> of ‘0 0 raster-image-width raster-image-height’). Where no value is readily available (e.g. an SVG file with no <code>viewBox</code> attribute on the outermost svg element) the <code>preserveAspectRatio</code> attribute is ignored, and only the translation due to the <code>x</code> & <code>y</code> attributes of the viewport is used to display the content.</p>
<p>For example, if the image element referenced a PNG or JPEG and <code>preserveAspectRatio="xMinYMin meet"</code>, then the aspect ratio of the raster would be preserved, the raster would be sized as large as possible while ensuring that the entire raster fits within the viewport, and the top/left of the raster would be aligned with the top/left of the viewport as defined by the attributes <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code> on the <code>image</code> element.<br />
If the value of <code>preserveAspectRatio</code> was ‘none’ then aspect ratio of the image would not be preserved. The image would be fitted such that the top/left corner of the raster exactly aligns with coordinate (<code>x</code>, <code>y</code>) and the bottom/right corner of the raster exactly aligns with coordinate (<code>x</code>+<code>width</code>, <code>y</code>+<code>height</code>).</p>
<h4 class="deeplink" id="using-iframe">Establishing a new viewport using `iframe`</h4>
<p>An <code>iframe</code> element that references an SVG file establishes new viewport similar to the situation of <code>image</code> element explained above. An <code>iframe</code> element can also have <code>x</code>, <code>y</code>, <code>width</code>, and <code>height</code> attributes, in addition to its own <code>preserveAspectratio</code>.</p>
<h4 class="deeplink" id="using-foreignobject">Establishing a new viewport using `foreignObject` </h4>
<p>A <code>foreignObject</code> element creates a new viewport for rendering the content that is within the element.</p>
<p>The <code>foreignObject</code> tag allows you to add non-SVG content into an SVG file. Usually, the contents of foreignObject are assumed to be from a different namespace. For example, you could drop some HTML in the middle of an SVG element.</p>
<p>The <code>foreignObject</code> element accepts attributes, among which are <code>x</code>, <code>y</code>, <code>height</code>, and <code>width</code>, which are used to position the object and size it, creating the bounds used to render the contents referenced inside it.</p>
<p>There is a lot to say about the <code>foreignObject</code> element besides its creation of a new viewport for its content. If you’re interested, you can check the <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject">MDN entry</a> or check <a href="http://thenittygritty.co/css-masking">this practical use case</a> by <a href="http://twitter.com/derschepp">Christian Schaeffer</a> on <a href="http://thenittygritty.co/">The Nitty Gritty Blog</a>.</p>
<h3 class="deeplink" id="wrapping-up">Wrapping Up</h3>
<p>Establishing new viewports and coordinate systems—be that by nesting <code>svg</code>s or another element from the ones mentioned above—allows you to control parts of the SVG that you would otherwise not be able to control the same way.</p>
<p>The entire time that I was working on this article and thinking of demos and use cases, all I kept thinking of is how nesting SVGs can give us finer control and flexibility for when we’re dealing with SVGs. Adaptive SVGs can be created with neat effects, fluid elements inside SVGs that are independent of the other elements on the page are possible, mimicing CSS border images for crispier backgrounds on high-resolution screens, and so much more.</p>
<p>Have you created any interesting examples using nested viewports in SVG? Can you think of more creative examples?</p>
<p>This article concludes the series of “Understanding SVG Coordinate Systems & Transformations”. Next up, we’ll be diving into animations, and more! Stay tuned, and thank you for reading!</p>
Understanding SVG Coordinate Systems and Transformations (Part 2) — The transform Attribute
2014-07-30T00:00:00Z
https://sarasoueidan.com/blog/svg-transformations/
<p class="deck">
SVG elements can be transformed by scaling, translating, skewing, and rotating—much like HTML elements can be transformed using CSS Transforms. However, there are certain inevitable differences when it comes to the coordinate systems used and affected by these transformations. In this article we'll go over the SVG <code>transform</code> attribute and CSS property, covering how to use it, and things you should know about SVG coordinate system transformations.
</p>
<p>This is the second article in a series I’m writing about SVG coordinate systems and transformations. In the first article, I covered everything you need to know to understand the basics of SVG coordinate systems; more specifically, the SVG viewport, and the <code>viewBox</code> and <code>preserveAspectRatio</code> attributes.</p>
<ul>
<li><a href="http://sarasoueidan.com/blog/svg-coordinate-systems">Understanding SVG Coordinate Systems & Transformations (Part 1) – The viewport, <code>viewBox</code>, and <code>preserveAspectRatio</code></a></li>
<li>Understanding SVG Coordinate Systems & Transformations (Part 2) – The <code>transform</code> Attribute</li>
<li><a href="http://sarasoueidan.com/blog/nesting-svgs">Understanding SVG Coordinate Systems & Transformations (Part 3) – Establishing New Viewports</a></li>
</ul>
<p>In this part I’m going to assume you read the first one, so, if you haven’t, make sure you do before you continue reading this article.</p>
<h3 class="deeplink" id="transform-attribute-values">The <code>transform</code> Attribute Values</h3>
<p>The <code>transform</code> attribute is used to specify one or more transformations on an element. It takes a <code><transform-list></code> as a value which is defined as a list of transform definitions, which are applied in the order provided. The individual transform definitions are separated by whitespace and/or a comma. An example of applying a transformation to an element may look like the following:</p>
<p>The possible SVG transformations are: <strong>rotation</strong>, <strong>scaling</strong>, <strong>translation</strong>, and <strong>skewing</strong>. The transformation functions used in the <code>transform</code> attribute work similar to the way CSS transform functions work in the <code>transform</code> property, except that they take different arguments.</p>
<p class="note update">
Note that the function syntax defined below only works in the <code>transform</code> attribute. See the <a href="https://sarasoueidan.com/blog/svg-transformations/#css-transformation-properties">section about transforming SVGs with CSS properties</a> for information on the syntax used in the CSS transform properties.
</p>
<h4 class="deeplink" id="matrix">Matrix</h4>
<p>You can apply one or more transformations to an SVG element using the <code>matrix()</code> function. The syntax for the matrix transformation is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">matrix(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>c</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>d</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>e</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>f</span><span class="token punctuation">></span></span>)</span></code></pre>
<p>The above declaration specifies a transformation in the form of a transformation matrix of six values. <code>matrix(a,b,c,d,e,f)</code> is equivalent to applying the transformation <strong>matrix [a b c d e f]</strong>.</p>
<p>For those of you who are not math-savvy, you’re probably not going to be using this function. Those of you who are, you can read more about the math behind it <a href="http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined">here</a>. Since this function is rarely used—if ever—I’m just going to skip to the other transformation functions.</p>
<h4 class="deeplink" id="translation">Translation</h4>
<p>To translate an SVG element, you can use the <code>translate()</code> function. The syntax for the translation function is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">translate(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tx</span><span class="token punctuation">></span></span> [<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ty</span><span class="token punctuation">></span></span>])</span></code></pre>
<p>The <code>translate()</code> function takes one or two values which specify the horizontal and vertical translation values, respectively. <code>tx</code> represents the translation value along the x-axis; <code>ty</code> represents the translation value along the y-axis.</p>
<p>The <code>ty</code> value is optional; and, if omitted, it defaults to zero. The <code>tx</code> and <code>ty</code> values can be either space-separated or comma-separated, and they don’t take any units inside the function—they default to the current user coordinate system units.</p>
<p>The following example translates an element by 100 user units to the right, and 300 user units to the bottom:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>translate(100 300)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The above example is still valid if the transformation was applied using <code>translate(100, 300)</code> where the values are comma-separated.</p>
<h4 class="deeplink" id="scaling">Scaling</h4>
<p>You can resize an SVG element by scaling it up or down using the <code>scale()</code> function transformation. The syntax for the scale transformation is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">scale(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>sx</span><span class="token punctuation">></span></span> [<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>sy</span><span class="token punctuation">></span></span>])</span></code></pre>
<p>The <code>scale()</code> function takes one or two values which specify the horizontal and vertical scaling values, respectively. <code>sx</code> represents the scaling value along the x-axis, used to stretch or shrink the element horizontally; <code>sy</code> represents the scaling value along the y-axis, used to stretch or shrink the element vertically.</p>
<p>The <code>sy</code> value is optional; and, if omitted, it is assumed to be equal to <code>sx</code>. The <code>sx</code> and <code>sy</code> values can be either space-separated or comma-separated, and they are unitless numbers.</p>
<p>The following example doubles the size of an element by scaling it to twice its original size:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>150<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>scale(2)<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The following stretches an element horizontally to 1.5 its original width, and shrinks it vertically to half its original height:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>150<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>scale(2 0.5)<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The above example is still valid if the transformation was applied using <code>scale(2, .5)</code> where the values are comma-separated.</p>
<p>It is important to note here that <strong>when an SVG element is scaled, its entire current coordinate system is scaled, resulting in the element also being repositioned inside the viewport</strong>. Don’t worry about this now, we’ll get into it in more detail in the next section.</p>
<h4 class="deeplink" id="skewing">Skew</h4>
<p>An SVG element can also be skewed. To skew it, you can use one or both of the two skew transformation functions: <code>skewX</code> and <code>skewY</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">skewX(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>skew-angle</span><span class="token punctuation">></span></span>)</span><br /><span class="highlight-line">skewY(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>skew-angle</span><span class="token punctuation">></span></span>)</span></code></pre>
<p>The <code>skewX</code> function specifies a skew transformation along the x-axis; and the <code>skewY</code> function specifies a skew transformation along the y-axis.</p>
<p>The skew angle specified is a <strong>unitless</strong> angle that defaults to degrees.</p>
<p>Note that skewing an element may result in the element being repositioned inside the viewport. More about this in the next section.</p>
<h4 class="deeplink" id="rotation">Rotation</h4>
<p>You can rotate an SVG element using the <code>rotate()</code> function. The syntax for the function is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">rotate(<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rotate-angle</span><span class="token punctuation">></span></span> [<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>cx</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>cy</span><span class="token punctuation">></span></span>])</span></code></pre>
<p>The <code>rotate()</code> function specifies a rotation by <code>rotate-angle</code> <strong>degrees</strong> about a given point. Unlike rotation transformations in CSS, you cannot specify an angle unit other than degrees. The angle value is specified <strong>unitless</strong>, and is considered a degrees value by default.</p>
<p>The optional <code>cx</code> and <code>cy</code> values represent the <strong>unitless</strong> coordinates of the point used as a center of rotation. If <code>cx</code> and <code>cy</code> are not provided, the rotation is about <strong>the origin of the current user coordinate system</strong>. (See <a href="http://sarasoueidan.com/blog/svg-coordinate-systems">Part 1</a> if you’re not sure what a user coordinate system is.)</p>
<p>Specifying a center of rotation inside the <code>rotate()</code> function is like a shorthand way for setting <code>transform: rotate()</code> and <code>transform-origin</code> in CSS. Since the default center of rotation in SVG is the upper left corner of the current user coordinate system in use, and since that may not allow you to create the rotation effect you want, you will probably end up specifying a new center inside <code>rotate()</code>. If you know your element’s dimensions and position in the SVG canvas, you can easily specify its center as the center of rotation.</p>
<p>The following example rotates a group of elements around a specified center of rotation positioned at (50, 50) in the current user coordinate system:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(45 50 50)<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- elements making up a parrot shape --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span></code></pre>
<p>However, if you want an element to rotate around its center, you’d probably rather specify the center as <code>50% 50%</code> like you would do in CSS; but unfortunately doing that inside the <code>rotate()</code> function is not possible—you need to use absolute coordinates. However, you <em>can</em> do this using the CSS <code>transform-origin</code> property in conjunction with the CSS <code>transform</code> property. More about this later in the article.</p>
<h3 class="deeplink" id="coordinate-system-transformations">Coordinate System Transformations</h3>
<p>Now that we’ve covered all possible SVG transformation functions, we’ll dig into the visual part and the effect of applying each transformation to an SVG element. This is the most important aspect of SVG transformations. And they are called “coordinate system transformations” not just “element transformations” for a reason.</p>
<p>In the <a href="http://www.w3.org/TR/SVG/coords.html">specification</a>, the <code>transform</code> attribute is defined as being one of the two attributes that <strong>establish a new user space (current coordinate system)</strong> on the element it is applied to — the <code>viewBox</code> attribute is the second of the two attributes that create a new user space. So what exactly does this mean?</p>
<blockquote class="pull-quote">
The <code>transform</code> attribute establishes a new user space (current coordinate system) on the element it is applied to.
</blockquote>
<p>This behavior is similar to the behavior of CSS transformations applied to an HTML element—the HTML element’s coordinate system is transformed, and this is usually most obvious when you’re chaining tranformations (we’ll get to this later). Despite being similar in many aspects, HTML and SVG transformations have some differences.</p>
<p>The main difference is the coordinate system. The coordinate system of an HTML element is established on the element itself. Meanwhile, in SVG, the coordinate system of an element is, initially, the current coordinate system or user space in use.</p>
<p>When you apply the <code>transform</code> attribute to an SVG element, that element gets a “copy” of the current user coordinate system in use. You can think of it as just creating a new “layer” for the transformed element, where the new layer has its own copy of the current user coordinate system (the <code>viewBox</code>).</p>
<p>Then, <strong>the element’s new current coordinate system is transformed by the transformation functions specified inside the <code>transform</code> attribute</strong>, thus resulting in the transformation of the element itself. It is as if the elements are drawn onto the canvas in the transformed coordinate system.</p>
<p>To understand how SVG transformations are applied, let’s start with the visual part. The following image shows the SVG canvas we’re going to be working with.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transforms-canvas.png" alt="" />
</figure>
<p>The parrot and the dog are the elements (groups <code><g></code>) that we’re going to be transforming.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the parrot --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the dog --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The grey coordinate system is the initial coordinate system of the canvas established by the <code>viewBox</code>. For simplicity’s sake, I’m going to not change the initial coordinate system—I’m using a <code>viewBox</code> that is the same size as the viewport, as you see in the above code.</p>
<blockquote class="pull-quote">
When you apply the <code>transform</code> attribute to an SVG element, that element gets a "copy" of the current user coordinate system in use.
</blockquote>
<p>Now that we’ve established our canvas and an initial user space, we’re going to start transforming elements. Let’s start by translating the parrot by 150 units to the left and 200 units downwards.</p>
<p>The parrot is, of course, made of several paths and shapes. It’s enough to apply the <code>transform</code> attribute to the group wrapping these shapes <code><g></code>; this will in turn apply the transformation to the entire set of shapes and paths, and the parrot will be translated as one whole item. See the <a href="http://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg">article on structuring and grouping SVGs</a> for more information.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>translate(150 200)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the parrot --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The following image shows the result of translating the parrot by the above translation. The translucent version of the parrot shows the initial position before the transformation was applied.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-translate.png" alt="" />
</figure>
<p>The translation transformation in SVG is as simple and straightforward as it is in CSS when applied on an HTML element. We mentioned earlier that applying the <code>transform</code> attribute to an element establishes a new current user coordinate system on it. The following image shows the “copy” of the initial coordinate system, that is established on the parrot element when it was transformed. Notice how the parrot’s current coordinate system is translated.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-translate-system.png" alt="" />
</figure>
<p>It’s important to notice here that <strong>the new current coordinate system established on the element is a replicate of the initial user space, with the position of the element preserved inside it. This means that it is <em>not</em> established on the element’s bounding box, nor is the size of the new current coordinate system restricted to the size of the element</strong>. This is where the difference between HTML and SVG coordinate system shines.</p>
<blockquote class="pull-quote">
The new current coordinate system established on a transformed element is <code>not</code> established on the element's bounding box, nor is its size restricted to the size of the element.
</blockquote>
<p>This is more evident if we are to transform the dog at the bottom right of the canvas. Suppose we want to translate the dog by 50 units to the right and then 50 units downwards. This is how the dog, its initial position, and the new current coordinate system (that is also translated with the dog) will look. Notice how the origin of the dog’s new current coordinate system is not positioned at the top left cornder of the dog’s bounding box. Also notice how the dog and its new coordinate system seem as if they were moved to a new “layer” on top of the canvas.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-translate-dog.png" alt="" />
</figure>
<p>Now let’s try something else. Instead of translating the parrot, let’s try scaling it. We’re going to scale the parrot to double its size:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>scale(2)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the parrot --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The result of scaling an SVG element differs from that of scaling an HTML element. The scaled SVG’s element’s position changes inside the viewport when it is scaled. The following image shows the result of doubling the size of the parrot. Notice the initial position and size, and the final size and position.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-scale.png" alt="" />
</figure>
<p>What we can notice from the above image is that not only the size (width and height) of the parrot were doubled, but the coordinates (x and y) were also multiplied by the scaling factor (which is two, here).</p>
<p>The reason we ended up with this result is something we’ve mentioned earlier: the element’s current coordinate system is transformed, and then the parrot is drawn into the new system. So, in this example, the current coordinate system was scaled. This effect is similar to the effect of using <code>viewBox = "0 0 400 300"</code>, which “zooms in” to the coordinate system, thus doubling the size of the content inside it (see <a href="http://sarasoueidan.com/blog/svg-coordinate-systems">part 1</a> of the series if you haven’t already).</p>
<p>So, if we were to visualize the coordinate system transformation showing the current transformed system of the parrot, we’d get the following result:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-scale-system.png" alt="" />
</figure>
<p>The new current coordinate system of the parrot is scaled up, “zooming in” to the parrot at the same time. Notice that, inside its current coordinate system, the parrot is not repositioned—it is only the effect of scaling the coordinate system that repositions it inside the viewport. The parrot is simply drawn at its original x and y coordinates inside the new scaled up system.</p>
<p>Let’s trying scaling the parrot in both directions using different scaling factors. If we scale the parrot by applying <code>transform="scale(2 0.5)</code>, we’re doubling its width while making it half its original height. The effect will be similar to applying <code>viewBox="0 0 400 1200"</code>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-scale-2.png" alt="" />
</figure>
<p>Notice the position of the parrot inside the scaled coordinate system, and compare it to the position in the initial system (translucent parrot): the x and y position coordinates are preserved.</p>
<p>Skewing an element in SVG also results in the element being “moved” as a result of its current coordinate system being skewed.</p>
<p>Suppose we apply a skew transformation to the dog along the x-axis using the <code>skewX</code> function. We’re going to skew the dog by 25 degrees horizontally.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dog<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>skewX(25)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the dog --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The following image shows the result of applying the skewing transformation to the dog. Its coordinate system is skewed, and so is the dog itself.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-skew-system.png" alt="" />
</figure>
<p>Note that the position of the dog with respect to its original position also changes, as a result of skewing its coordinate system.</p>
<p>The following image shows the result of skewing the dog by the same angle using <code>skewY()</code> instead of <code>skewX</code>:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-skew-system-2.png" alt="" />
</figure>
<p>And last but not least, let’s try rotating the parrot. The default center of rotation is the upper left corner of the current user coordinate system. The new current system established on the rotated element will also be rotated. In the following example, we’re going to rotate the parrot by 45 degrees. The positive direction of rotation is clockwise.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(45)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the parrot --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The result of applying the above transformation looks like this:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-rotate.png" alt="" />
</figure>
<p>You are probably going to want to rotate an element around a point other than the default origin of the coordinate system. Using the <code>rotate()</code> function in the <code>transform</code> attribute, you can specify that point explicitly. Suppose we want to rotate the parrot in this example around its own center. According to the width, height, and position of the parrot, I can determine its center to be at approximately (150, 170). The parrot can then be rotated around this point:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(45 150 170)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the parrot --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>At this point, the parrot is rotated and will look like so:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-rotate-center.png" alt="" />
</figure>
<p>We said that the transformations are applied to the coordinate system, and because of that, the element is eventually affected and transformed. So how exactly does changing the center of rotation work on the coordinate system whose origin is at the point (0, 0)?</p>
<p>When you change the center or rotation, the coordinate system is translated, rotated by the specified angle, and then translated again by specific values depending on the center of rotation you specify. In this example, this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(45 150 170)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span></code></pre>
<p>is performed by the browser as a series of translation and rotation operations equivalent to:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>parrot<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>translate(150 170) rotate(45) translate(-150 -170)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span></code></pre>
<p>The current coordinate system is translated to the point you want to be the center. It is then rotated by the angle you specify. And then finally the system is translated by the negation of the values. The above transformation applied on the system looks like this:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-rotate-center-system.png" alt="" />
</figure>
<p>Before we move on to the next section where we’re going to nest and chain transformations, I want to note that the current user coordinate system established on a transformed element is independent from other coordinate systems established on other transformed elements. The following image shows the two coordinate systems established on the dog and the parrot, and how they are independent from each other.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-multiple.png" alt="" />
<figcaption>
The new current user coordinate systems established on the parrot and the dog when they are transformed.
</figcaption>
</figure>
<p>Also note that each current coordinate system still lies inside the main coordinate system of the canvas established using the <code>viewBox</code> attribute on the containing <code><svg></code>. Any transformations applied to the <code>viewBox</code> will affect the entire canvas and all elements inside it as well, whether they have their own established coordinate systems or not.</p>
<p>For example, the following is the result of changing the user space of the entire canvas from <code>viewBox="0 0 800 600"</code> to <code>viewBox="0 0 600 450"</code>. The entire canvas is scaled up, preserving any transformations applied to the individual elements.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-multiple-2.png" alt="" />
<figcaption>
The result of changing the user coordinate system on the entire canvas.
</figcaption>
</figure>
<h4 class="deeplink" id="nested-and-chained-transformations">Nested and Chained Transformations</h4>
<p>A lot of times you may want to apply several transformations to an element. Applying multiple transformations in a row is what is referred to as “chaining” transformations.</p>
<p>When transformations are chained, the most important thing to be aware of is that, just like with HTML element transformations, each transformation is applied to the coordinate system after that system is transformed by the previous transformations.</p>
<p>For example, if you’re going to apply a rotation to an element, followed by a translation, the translation happens according to the new coordinate system, not the inital non-rotated one.</p>
<p>The following example does just that. We’re applying the previous rotation, and then translating the parrot using by 200 units along the positive x-axis<code>transform="rotate(45 150 170) translate(200)"</code>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/svg-transformations-rotate-translate.png" alt="" />
</figure>
<p>Depending on the final position and transformation you’re after, you need to chain your transformations accordingly. Always keep the coordinate system in mind.</p>
<p>Note that when you skew an element—and its coordinate system with it—the coordinate system will no longer be an orthogonal one, and the coordinates will no longer be calculated as orthogonal ones—they will be <a href="http://en.wikipedia.org/wiki/Skew_coordinates">skew coordinates</a>. Simply put, this just means that the coordinate system’s origin is no longer a 90 degrees angle, and hence the new coordinates will be computed based on this new angle.</p>
<p>Nested transformations occur when a child of a transformed element is also transformed. The transformation applied to the child element will be an accumulation of the transformations applied on its ancestors and the transformation applied on it.</p>
<blockquote class="pull-quote">
[For nested transformations,] the transformation applied to the child element will be an accumulation of the transformations applied on its ancestors and the transformation applied on it.
</blockquote>
<p>So, in effect, nesting transforms is similar to chaining them; only difference is that instead of applying a series of transformations on one element, it automatically gets the transformations applied on its acestors, and then finally its own transformations are applied to it, just like we applied transformations in the chain above—one after the other.</p>
<p>This is particularly useful for when you want to transform one element relative to another. For example, suppose you want to animate the tail of the dog. The tail is a descendant of the <code>#dog</code> group.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dog<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>translate(..)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- shapes and paths forming the dog --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>head<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- .. --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(.. .. ..)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tail<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rotate(..)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- animateTransform here --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>legs<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Suppose we translate the dog group; then rotate its body by some angle around some point, and then we want to animate the tail by rotating it and animating that rotation.</p>
<p>When the tail is to be rotated, it “inherits” the transformed coordinate system of its ancestor (<code>#body</code>), which in turn inherits the transformed coordinate system of its transformed ancestor (<code>#dog</code>) as well. So, in effect, when the taill is rotated, it is as though it has been translated (by the same translation of the <code>#dog</code> group), then rotated (by the same rotation of the <code>#body</code> group), and <em>then</em> rotated by its own rotation. And the effect of applying a series of transformations here is the same as we explained in the chaining example above.</p>
<p>So, you see, nesting transformations has practically the same effect as chaining them on the <code>#tail</code>.</p>
<h3 class="deeplink" id="transforming-svgs-with-css"> Transforming SVGs using CSS Properties</h3>
<p>In SVG 2, the <code>transform</code> attribute is referred to as the <code>transform</code> property; this is because SVG transformations have been “exported” into the <a href="http://dev.w3.org/csswg/css-transforms/">CSS3 Transforms specification</a>. The latter combines the SVG Transforms, CSS 2D Transforms, and CSS 3D Transforms specifications, and introduces features like <code>transform-origin</code> and 3D transformations into SVG.</p>
<p>The CSS transform properties specified in the CSS Transforms specifications can be applied to SVG elements. However, the values for the <code>transform</code> property functions need to follow the syntax specified in the CSS spec: function arguments must be separated with commas — space-separation alone isn’t valid, but you can include one or more white space before and/or after the comma; and the <code>rotate()</code> function does not take <code><cx> <cy></code> values anymore — the center of rotation is specified using the <code>transform-origin</code> property. Also, the CSS transformatio functions do accept units for angles and coordinates, such as <code>rad</code> (radians) for angles (among others) and <code>px</code>, <code>em</code>, etc. for coordinate values.</p>
<p>An example of rotating an SVG element using CSS may look like the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">#parrot</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50%<span class="token punctuation">;</span> <span class="token comment">/* center of rotation is set to the center of the element */</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>45deg<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And SVG element can also be transformed in three-dimensional space using CSS 3D Transforms. Note that the coordinate systems are still, however, different from the coordinate systems established on HTML elements. This means that 3D rotations will also look different unless you change the center of rotation.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">#SVGel</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">perspective</span><span class="token punctuation">(</span>800px<span class="token punctuation">)</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>1<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 45deg<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Because transforming SVG elements with CSS is pretty much the same as transforming HTML elements with CSS—syntax-wise—I’m going to skip elaborating on this topic in this article.</p>
<p>That said, at the time of writing of this article, implementations are still incomplete in some browsers and for some features. Because of how fast browser support changes, I recommend you experiment with the properties to determine what currently works and what doesn’t, and decide on what you can start using today and what not.</p>
<p>Note that once CSS Transforms are fully implemented for SVG elements, it is recommended that you use the CSS transforms function syntax even when you apply the transformation in the form of a <code>transform</code> attribute. That said, the above mentioned syntax of the <code>transform</code> attribute functions will still be valid.</p>
<h3 class="deeplink" id="animating-transform">Animating <code>transform</code></h3>
<p>SVG transformations can be animated, just like CSS transforms can be. If you’re using the CSS <code>transform</code> property to transform the SVG, you can animate the transformation using CSS animations and transitions just like you would animate CSS transforms on HTML elements.</p>
<p>The SVG <code>transform</code> attribute can be animated using the SVG <code><animateTransform></code> element. The <code><animateTransform></code> element is one of three elements in SVG that are used to animate different SVG attributes.</p>
<p>Details of the <code><animateTransform></code> element are outside the scope of this article. Stay tuned for an article I’ll be writing about the different SVG animation elements, including <code><animateTransform></code>.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>Working with SVGs can be really frustrating at first, if the concepts behind the coordinate system transformations aren’t very clear, especially if you’re coming from a CSS HTML transformations background, and naturally expect SVG elements to respond the same way to transformations as HTML elements do.</p>
<p>However, once you get a grip of how they work, you gain a better control over your SVG canvas, and can manipulate elements more easily.</p>
<p>In the last part of this series, I’m going to go over nesting SVGs and establishing new viewports and viewboxes. Stay tuned!</p>
<p class="note">
The SVG parrot & dog illustrations used are freebies from <a href="http://freepik.com/">Freepik.com</a>.
</p>
<p>I hope you liked this article and found it useful. Thank you for reading!</p>
Understanding SVG Coordinate Systems and Transformations (Part 1) — The viewport, viewBox, and preserveAspectRatio
2014-07-17T00:00:00Z
https://sarasoueidan.com/blog/svg-coordinate-systems/
<p class="deck">SVG elements aren't governed by a CSS box model like HTML elements are. This makes positioning and transforming these elements trickier and may seem—at first glance—less intuitive. However, once you understand how SVG coordinate systems and transformations work, manipulating SVGs becomes a lot easier and makes a lot more sense. In this article we're going to go over three of the most important SVG attributes that control SVG coordinate systems: <code>viewport</code>, <code>viewBox</code>, and <code>preserveAspectRatio</code>.</p>
<p>This is the first in a series of three articles covering the topic of coordinate systems and transformations in SVG.</p>
<ul>
<li>Understanding SVG Coordinate Systems & Transformations (Part 1) – The viewport, <code>viewBox</code>, & <code>preserveAspectRatio</code></li>
<li><a href="http://sarasoueidan.com/blog/svg-transformations">Understanding SVG Coordinate Systems & Transformations (Part 2) – The <code>transform</code> Attribute</a></li>
<li><a href="http://sarasoueidan.com/blog/nesting-svgs">Understanding SVG Coordinate Systems & Transformations (Part 3) – Establishing New Viewports</a></li>
</ul>
<p>For the sake of visualizing the concepts and explanations in the article even further, I created an interactive demo that allows you to play with the values of the <code>viewBox</code> and <code>preserveAspectRatio</code> attributes.</p>
<p><a class="btn" href="https://sarasoueidan.com/demos/interactive-svg-coordinate-system/index.html">Check the interactive demo out.</a></p>
<p class="note">The demo is the cherry on top of the cake, so do make sure you come back to read the article if you check it out before you do!</p>
<h3 class="deeplink" id="svg-canvas">The SVG Canvas</h3>
<p>The <strong>canvas</strong> is the space or area where the SVG content is drawn. Conceptually, this canvas is infinite in both dimensions. The SVG can therefore be of any size. However, it is rendered on the screen relative to a <strong>finite region</strong> known as <em>the viewport</em>. Areas of the SVG that lie beyond the boundaries of the viewport are clipped off and not visible.</p>
<h3 class="deeplink" id="svg-viewport">The viewport</h3>
<p>The viewport is the viewing area where the SVG will be visible. You can think of the viewport as a window through which you can see a particular scene. The scene may be entirely or partially visible through that window.</p>
<p>The SVG viewport is similar to the viewport of the browser you’re viewing this page through. A web page can be of any size; it can be wider than the viewport’s width, and is in most cases also longer than the viewport’s length. However, only portions of a web page are visible through the viewport at a time.</p>
<p class="note">Whether or not the entire SVG canvas or part of it is visible depends on the <strong>size of that canvas*</strong> and <strong>the value of the <code>preserveAspectRatio</code> attribute</strong>. You don't have to worry about these now; we'll talk about them further in more detail.</p>
<p>You specify the size of the viewport using the <code>width</code> and <code>height</code> attributes on the outermost <code><svg></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- the viewport will be 800px by 600px --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>In SVG, values can be set with or without a unit identifier. A unitless value is said to be specified <em>in user space</em> using <em>user units</em>. If a value is specified in user units, then the value is assumed to be equivalent to the same number of “px” units. This means that the viewport in the above example will be rendered as a 800px by 600px viewport.</p>
<p>You can also specify values using units. The supported length unit identifiers in SVG are: <code>em</code>, <code>ex</code>, <code>px</code>, <code>pt</code>, <code>pc</code>, <code>cm</code>, <code>mm</code>, <code>in</code>, and percentages.</p>
<p>Once the width and height of the outermost SVG element are set, the browser establishes an initial <em>viewport coordinate system</em> and an initial <em>user coordinate system</em>.</p>
<h4 class="deeplink" id="initial-coordinate-system">The initial coordinate system</h4>
<p>The initial <strong>viewport coordinate system</strong> is a coordinate system established on the viewport, with the origin at the top left corner of the viewport at point (0, 0), the positive x-axis pointing towards the right, the positive y-axis pointing down, and one unit in the initial coordinate system equals one “pixel” in the viewport. This coordinate system is similar to the coordinate system established on an HTML element with a CSS box model.</p>
<p>The initial <strong>user coordinate system</strong> is the coordinate system established on the SVG canvas. This coordinate system is initially identical to the viewport coordinate system—it has its origin at the top left corner of the viewport with the positive x-axis pointing towards the right, the positive y-axis pointing down. Using the <code>viewBox</code> attribute, the initial user coordinate system—also known as <strong>the current coordinate system</strong>, or <strong>user space in use</strong>—can be modified so that it is not identical to the viewport coordinate system anymore. We’ll talk about modifying it in the next section.</p>
<p>For now, we won’t specify a <code>viewBox</code> attribute value. The user coordinate system of the SVG canvas is identical to that of the viewport.</p>
<p>In the following image, the viewport coordinate system “ruler” is grey, and that of the user coordinate system (the <code>viewBox</code>) is blue. Since they are both identical at this point, the two coordinate systems overlap.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/initial-coordinate-systems.jpg" alt="Viewport Coordinate System" />
<figcaption>
The initial coordinate systems established on the viewport and SVG canvas. Grey units represent the viewport coordinate system; blue units represent the user coordinate system. Both coordinate systems are identical and coincide.
</figcaption>
</figure>
<p>The parrot in the above SVG has a bounding box that is 200 units (200 pixels in this case) in width and 300 units in height. The parrot is drawn on the canvas based on the initial coordinate system.</p>
<p class="note">A new user space (i.e., a new current coordinate system) can also be established by specifying <strong>transformations</strong> using the <code>transform</code> attribute on a container element or graphics element. We'll talk about transformations in the second part of this article, and then in more details in the third and last part.</p>
<h3 class="deeplink" id="svg-viewbox">The <code>viewBox</code></h3>
<p>I like to think of the <code>viewBox</code> as the “real” coordinate system. After all, it is <em>the</em> coordinate system used to draw the SVG graphics onto the canvas. This coordinate system can be smaller or bigger than the viewport, and it can be fully or partially visible inside the viewport too.</p>
<p>In the previous section, this coordinate system—the user coordinate system—was identical to the viewport coordinate system. The reason for that is that we did not specify it to be otherwise. That’s why all the positioning and drawing seemed to be done relative to the viewport coordinate system. Because once we created a viewport coordinate system (using <code>width</code> and <code>height</code>), the browser created a default user coordinate system that is identical to it.</p>
<p>You specify your own user coordinate system using the <code>viewBox</code> attribute. If the user coordinate system you choose has the same aspect ratio (ratio of height to width) as the viewport coordinate system, it will stretch to fill the viewport area (we’ll talk examples in a minute). However, if your user coordinate system does not have the same aspect ratio, you can use the <code>preserveAspectRatio</code> attribute to specify whether or not the entire system will be visible inside the viewport or not, and you can also use it to specify how it is positioned inside the viewport. We’ll get into details and lots of examples for this case in the next section. In this section, we’ll stick to examples where the aspect ratio of the <code>viewBox</code> matches that of the viewport—in these examples, <code>preserveAspectRatio</code> has no effect.</p>
<p>Before we get into the examples, we’ll go over the syntax of <code>viewBox</code>.</p>
<h4 class="deeplink" id="viewbox-syntax">The <code>viewBox</code> syntax</h4>
<p>The <code>viewBox attribute</code> takes four parameters as a value: <code><min-x></code>, <code><min-y></code>, <code>width</code> and <code>height</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">viewBox = <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>min-x</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>min-y</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>width</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>height</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code><min-x></code> and <code><min-y></code> values determine the upper left corner of the viewbox, and the <code>width</code> and <code>height</code> determine the width and height of that viewBox. Note here that the width and height of the viewBox need not be the same as the width and height set on the parent <code><svg></code> element. A negative value for <code><width></code> or <code><height></code> is invalid. A value of zero disables rendering of the element.</p>
<p class="note">
Note that the width of the viewport can also be set in CSS to any value. For example, setting <code>width: 100%</code> will make the SVG viewport fluid in a document. Whatever value of the <code>viewBox</code>, it will then be mapped to the computed width of the outer SVG element.
</p>
<p>An example of setting <code>viewBox</code> would look like the following:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- The viewbox in this example is equal to the viewport, but it can be different --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you’ve read about the <code>viewBox</code> somewhere before, you may have come across a few definitions saying that you can use the <code>viewBox</code> attribute to transform the SVG graphic by scaling or translating it. This is true. I’m going to go further and say that you can even <em>crop</em> the SVG graphic using <code>viewBox</code>.</p>
<p>The best way to understand the <code>viewBox</code> and differentiate it from the viewport is by visualizing it. So let’s start with some examples. We’ll start with examples where the aspect ratio of the viewbox is the same as the aspect ratio of the viewport, so we won’t need to dig into <code>preserveAspectRatio</code> yet.</p>
<h4 class="deeplink" id="viewbox-aspect-ratio"><code>viewBox</code> with aspect ratio equal to the viewport's aspect ratio</h4>
<p>We’ll start with a simple example. The <code>viewbox</code> in this example will be half the size of the viewport. We won’t change the origin of the viewbox in this one, so both <code><min-x></code> and <code><min-y></code> will be set to zero. The width and height of the viewbox will be half the width and height of the viewport. This means that we’re preserving the aspect ratio.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 400 300<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>So, what does <code>viewbox="0 0 400 300"</code> exactly do?</p>
<ul>
<li>It specifies a specific region of the canvas spanning from a top left point at (0, 0) to a point at (400, 300).</li>
<li>The SVG graphic is then <strong>cropped</strong> to that region.</li>
<li>The region is <strong>scaled up</strong> (in a zoom-in-like effect) to fill the entire viewport.</li>
<li>The user coordinate system is mapped to the viewport coordinate system so that—in this case—one user unit is equal to two viewport units.</li>
</ul>
<p>The following image shows the result of applying the above viewbox to the <code><svg></code> canvas in our example. The grey units represent the viewport coordinate system, and the blue coordinate system represents the user coordinate system established by the <code>viewBox</code>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-400-300-crop.jpg" alt="" />
<figcaption>
Specifying a viewbox has a result similar to cropping the graphic to that viewbox and then zooming it in so that it fills the entire viewport area. Remember that we're still maintaining the same aspect ratio as the viewport in this case.
</figcaption>
</figure>
<p>Anything you draw on the SVG canvas will be drawn relative to the new user coordinate system.</p>
<p class="note">I like to visualize the SVG canvas with a <code>viewBox</code> the same way as Google maps. You can zoom in to a specific region or area in Google maps; that area will be the only area visible, scaled up, inside the viewport of the browser. However, you know that the rest of the map is still there, but it's not visible because it extends beyond the boundaries of the viewport—it's being clipped out.</p>
<p>Now let’s try changing the <code><min-x></code> and <code><min-y></code> values. We’ll set both to <code>100</code>. They can be any number you want. The width and height ratio will also be the same as width and height ratio of the viewport.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100 100 200 150<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The effect of applying <code>viewBox="100 100 200 150"</code> is also a crop effect like the one in the previous example. The graphic is cropped and scaled up to fill the entire viewport area.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-200-150-crop.jpg" alt="" />
<figcaption>
The result of "cropping" the graphic to a viewbox with an origin at (100, 100) and width 200 and height 150.
</figcaption>
</figure>
<p>Again, the user coordinate system is mapped to the viewport coordinate system—200 user units are mapped to 800 viewport units so that every user unit is equal to four viewport units. This results in a zoom-in effect like the one you can see in the above screenshot.</p>
<p>Also note, at this point, that specifying non-zero values for the <code><min-x></code> and <code><min-y></code> values has a transformation effect on the graphic; more specifically, it is as though the SVG canvas was translated by 100 units to the top and 100 units to the left (<code>transform="translate(-100 -100)"</code>).</p>
<p>Indeed, as the specification states, <q>the effect of the <code>viewBox</code> attribute is that the user agent automatically supplies the appropriate transformation matrix to map the specified rectangle in user space to the bounds of a designated region (often, the viewport)</q>.</p>
<p>This is just a fancy way of saying what we already mentioned before: the graphic is cropped and then <strong>scaled</strong> to fit into the viewport. The spec then goes on to add a note: <q>in some cases the user agent will need to supply a <strong>translate transformation</strong> in addition to a scale transformation. For example, on an outermost svg element, <strong>a translate transformation will be needed if the <code>viewBox</code> attributes specifies values other than zero for <code><min-x></code> or <code><min-y></code></strong>.)</q></p>
<p>To demonstrate the translation transformation even better, let’s try applying negative values (-100) to <code><min-x></code> and <code><min-y></code>. The translation effect would be then similar to <code>transform="translate(100 100)"</code>; meaning that the graphic will be translated to the bottom and to the right after being cropped and scaled. If were to revisit the second to last example with a crop size of 400 by 300, and then add the new negative <code><min-x></code> and <code><min-y></code> values, this would be our new code:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-100 -100 400 300<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The result of applying the above <code>viewBox</code> transformation to the graphic is shown in the following image:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-400-300-crop-translate.jpg" alt="" />
<figcaption>
<p></p></figcaption><p></p>
</figure>
<p>Note that, unlike the <code>transform</code> attribute, the automatic transformation that is created due to a <code>viewBox</code> does not affect the <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code> attributes on the element with the <code>viewBox</code> attribute. Thus, in the example above which shows an <code>svg</code> element which has attributes <code>width</code>, <code>height</code> and <code>viewBox</code>, the <code>width</code> and <code>height</code> attributes represent values in the coordinate system that exists before the <code>viewBox</code> transformation is applied. You can see this in the above examples as the initial (grey) viewport coordinate system remains unaffected even after using the <code>viewBox</code> attribute on the <code><svg></code>.</p>
<p>On the other hand, like the <code>transform</code> attribute, it does establish a new coordinate system for all other attributes and for descendant elements. You can also see that in the above examples as the user coordinate system established is a new one—it does not remain as the initial user coordinate system which was identical to the viewport coordinate system before the <code>viewBox</code> was used. And any descendants of the <code><svg></code> will be positioned and sized in the <strong>new</strong> user coordinate system, not the initial one.</p>
<p>Our last <code>viewBox</code> example is similar to the previous ones, but instead of cropping the canvas, we’re going to <em>extend</em> it inside the viewport and see how it affects the graphic. We’re going to specify a viewbox with a width and height that are larger than those of the viewport, while also maintaining the aspect ratio of the viewport. We’ll deal with different aspect ratios in the next section.</p>
<p>In this example, we’ll make the viewbox 1.5 times the size of the viewport.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 1200 900<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- SVG content drawn onto the SVG canvas --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>What will happen now is that the user coordinate system is going to be scaled up to 1200x900. It will then be mapped to the viewport coordinate system so that every 1 unit in the user coordinate system is equal to <code>viewport-width / viewBox-width</code> horizontally, and <code>viewport-height / viewBox-height</code> units vertically in the viewport coordinate system. This means that, in this case, every one x-unit in the user coordinate system is equal to 0.66 x-units in the viewport coordinate system, and every one user y-unit is mapped to 0.66 viewport y-units.</p>
<p>Of course, the best way to understand this is to visualize the result. The viewbox is scaled so that it fits inside the viewport as shown in the following image. And because the graphic is drawn on the canvas based on the new user coordinate system, not the viewport coordinate system, it will look smaller inside the viewport.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-1200-900.jpg" alt="" />
<figcaption>
A 1200x900 user coordinate system mapped into the 800x600 viewport coordinate system. The grey units represent the viewport coordinate system; the blue units represent the system established from the <code>viewBox</code>.
</figcaption>
</figure>
<p>So far, all of our examples have been in conformity with the viewport’s height to width aspect ratio. But what happens if the height and width specified in the <code>viewBox</code> have a different aspect ratio than that of the viewport’s? For example, suppose we set the dimensions of the viewbox to be 1000x500. The aspect ratio of height to width is no longer the same as that of the viewport. The result of using <code>viewBox = "0 0 1000 500"</code> in our example looks like the following:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-1000-500.jpg" alt="" />
<figcaption>
The result of defining a 1000x500 user coordinate system in a 800x600 viewport.
</figcaption>
</figure>
<p>The user coordinate system and hence the graphic is positioned inside the viewport so that:</p>
<ul>
<li>The entire viewbox fits inside the viewport.</li>
<li>The aspect ratio of the viewbox is preserved. The viewbox was <em>not</em> stretched to cover the viewport area.</li>
<li>THe viewbox is centered inside the viewport both vertically and horizontally.</li>
</ul>
<p>This is the default behavior. What controls this behavior? And what if we want to change the position of the viewbox inside the viewport? This is where the <code>preserveAspectRatio</code> attribute comes in.</p>
<h3 class="deeplink" id="preserveaspectratio">The <code>preserveAspectRatio</code> Attribute</h3>
<p>The <code>preserveAspectRatio</code> attribute is used to force a uniform scaling for the purposes of preserving the aspect ratio of a graphic.</p>
<p>If you define a user coordinate system with an aspect ratio different from that of the viewport’s, and if the browser were to stretch the viewbox to fit into the viewport as we’ve seen in previous examples, the difference in aspect ratios will cause the graphic to be distorted in either direction. So if the viewbox in the last example were to be stretched to fill the viewport in both directions, the graphic would look like so:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-1000-500-stretched.jpg" alt="" />
<figcaption>
The result of mapping the user coordinate system to the viewport without preserving its aspect ratio. The graphic is distorted and looks shrunk horizontally while also being stretched vertically.
</figcaption>
</figure>
<p>The distortion is also clearly visible (and unwanted, of course) when using a viewbox value of <code>0 0 200 300</code>, which would be smaller than the dimensions of the viewport. I chose this value in particular so that the viewbox matches the size of the bounding box of the parrot. If the browser were to stretch the graphic to fill the entire viewport, it would look like the so:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-200-300-stretched.jpg" alt="" />
<figcaption>
The result of mapping the user coordinate system to the viewport without preserving its aspect ratio. The graphic is distorted.
</figcaption>
</figure>
<p>The <code>preserveAspectRatio</code> attribute allows you to force uniform scaling of the viewbox, while maintaining the aspect ratio, and it allows you to specify how to position the viewbox inside the viewport if you don’t want it to be centered by default.</p>
<h4 class="deeplink" id="preserveAspectRatio-syntax">The <code>preserveAspectRatio</code> syntax</h4>
<p>The official syntax for <code>preserveAspectRatio</code> is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">preserveAspectRatio = defer? <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>align</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meetOrSlice</span><span class="token punctuation">></span></span>?</span></code></pre>
<p>It is usable on any element that establishes a new viewport (we’ll get into these in the next parts of the series).</p>
<p>The <code>defer</code> argument is optional, and is used only when you’re applying <code>preserveAspectRatio</code> to an <code><image></code>. It is ignored when used on any other element. Since <code><image></code> it outside the scope of this article, we’ll skip the <code>defer</code> option for now.</p>
<p>The <code>align</code> parameter indicates whether to force uniform scaling and, if so, the alignment method to use in case the aspect ratio of the <code>viewBox</code> doesn’t match the aspect ratio of the viewport.</p>
<p>If the <code>align</code> value is set to <code>none</code>, for example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">preserveAspectRatio = "none"</span></code></pre>
<p>The graphic will be scaled to fit inside the viewport without maintaining the aspect ratio, just like we saw in the last two examples.</p>
<p>All other values of <code>preserveAspectRatio</code> force uniform scaling while preserving the viewbox’s aspect ratio, and specify how to align the viewbox inside the viewport. We’ll get into the values of <code>align</code> shortly.</p>
<p>The last argument, <code>meetOrSlice</code> is also optional, and it defaults to <code>meet</code>. This argument specifies whether or not the entire <code>viewBox</code> should be visible inside the viewport. If provided, it is separated from the <code>align</code> parameter by one or more spaces. For example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">preserveAspectRatio = "xMinYMin slice"</span></code></pre>
<p>These values may seem foreign at first. To make understanding them easier and make them more familiar, you can think of the <code>meetOrSlice</code> value as being similar to the <code>background-size</code> values <code>contain</code> and <code>cover</code>; they work pretty much the same. <code>meet</code> is similar to <code>contain</code>, and <code>slice</code> is similar to <code>cover</code>. Here are the definitions and meaning of each value:</p>
<dl>
<dt>meet (The default value)</dt>
<dd>
Scale the graphic as much as possible while maintaining the following two criteria:
<ul>
<li>aspect ratio is preserved</li>
<li>the entire <code>viewBox</code> is visible within the viewport</li>
</ul>
<p>
In this case, if the aspect ratio of the graphic does not match the viewport, some of the viewport will extend beyond the bounds of the <code>viewBox</code> (i.e., the area into which the <code>viewBox</code> will draw will be smaller than the viewport). (See the last example of The viewBox section.) In this case, the boundaries of the `viewBox` are contained inside the viewport such that the boundaries *meet*.
</p>
<p class="note">
This value is similar to <code>background-size: contain</code>. The background image is scaled as much as possible while preserving its aspect ratio and making sure it fits entirely into the background painting area. If the aspect ratio of the background is not the same as that of the element it is being applied to, parts of the background painting area will not be covered by the background image.
</p>
</dd>
<dt>slice</dt>
<dd>
Scale the graphic so that the <code>viewBox</code> covers the entire viewport area, while maintaining its aspect ratio. The <code>viewBox</code> is scaled <strong>just enough</strong> to cover the viewport area (in both dimensions), but it is not scaled any more than needed to achieve that. In other words, it is scaled to the smallest size such that the width and height of the <code>viewBox</code> can completely cover the viewport.
<p>
In this case, if the aspect ratio of the <code>viewBox</code> does not match the viewport, some of the <code>viewBox</code> will extend beyond the bounds of the viewport (i.e., the area into which the <code>viewBox</code> will draw is larger than the viewport). This will result in part of the `viewBox` being *sliced off*.
</p>
<p class="note">
You can think of this as being similar to <code>background-size: cover</code>. In the case of a background image, the image is scaled while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
</p>
</dd>
</dl>
<p>So, <code>meetOrSlice</code> is used to specify whether or not the <code>viewBox</code> will be completely contained inside the viewport, or if it should be scaled as much as needed to cover the entire viewport, even if this means that a part of the viewbox will be “sliced off”.</p>
<p>For example, if we were to apply <code>viewBox</code> size of 200x300, and using both the <code>meet</code> and <code>slice</code> values, keeping the <code>align</code> value set to the default by the browser, the result for each value will look like the following:</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-200-300-meet-vs-slice.jpg" alt="" />
<figcaption>
The result of applying the <code>meet</code> parameter vs the <code>slice</code> parameter on a <code>viewBox</code> with an aspect ratio different from that of the viewport's aspect ratio.
<br />
<br />
The default value for <code>align</code> is <code>xMidYMid</code>, so, in both cases, the graphic is scaled so that its mid axes align with the mid axes of the viewport.
</figcaption>
</figure>
<p>The <code>align</code> parameter takes one of nine values, or the <code>none</code> value. Any value other than <code>none</code> is used to uniformly scale the image preserving its aspect ratio, <em><strong>and</strong></em> it is also used to <em>align</em> the <code>viewBox</code> inside the viewport.</p>
<p>The <code>align</code> values works similar to the way <code>background-position</code> works when used with percentage values. You can think of the <code>viewBox</code> as being the background image. The way the positioning with <code>align</code> differs from <code>background-position</code> is that instead of positioning a specific point of the viewbox over a corresponding point of the viewport, it <em>aligns</em> specific “axes” of the viewBox with corresponding “axes” of the viewport.</p>
<p>In order to understand the meaning of each of the <code>align</code> values, we’re going to first introduce each of the “axes”.</p>
<p>Remember the <code><min-x></code> and <code><min-y></code> values of the <code>viewBox</code>? We’re going to use each of these to define the “min-x” axis and “min-y” axis on the <code>viewBox</code>. Additionally, we’re going to define two axes “max-x” and “max-y”, which will be positioned at <code><min-x> + <width></code> and <code><min-y> + <height></code>, respectively. And last but not least, we’ll define two axes “mid-x” and “mid-y”, which are positioned at <code><min-x> + (<width>/2)</code> and <code><min-y> + (<height>/2)</code>, respectively.</p>
<p>Did that make things more confusing? If so, have a look at the following image to see what each of those axes represents. In the image, both <code><min-x></code> and <code><min-y></code> are set to their default <code>0</code> values. The <code>viewBox</code> is set to <code>viewBox = "0 0 300 300"</code>.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-x-y-axes.jpg" alt="" />
<figcaption>
The pink and orange solid lines represent the min-y and min-x values respectively. The dashed pink and orange lines represent the mid and max x and y values.
</figcaption>
</figure>
<p>The dashed grey lines in the above image represent the mid-x and mid-y axes of the viewport. We’re going to use those to align the mid-x and mid-y of axes of the <code>viewBox</code> for some values. For the viewport, the min-x value is equal to 0, the min-y value is also 0, the max-x value is equal to the width of the <code>viewBox</code>, the max-y value is equal to its height, and the mid-x and mid-y represent the middle values of the width and height, respectively.</p>
<p>The alignment values are:</p>
<dl>
<dt>none</dt>
<dd>
<p>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly (without preserving aspect ratio) if necessary such that the element’s bounding box exactly matches the viewport rectangle.</p>
<p>In other words, the <code>viewBox</code> is stretched or shrunk as necssary so that it fills the entire viewport exactly, disregarding the aspect ratio. The graphic may be distorted.</p>
<p>(Note: if <code>align</code> is none, then the optional <code>meetOrSlice</code> value is ignored.)</p>
</dd>
<dt>xMinYMin</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code> of the element’s <code>viewBox</code> with the smallest X value of the viewport.</p>
<p>Align the <code>min-y</code> of the element’s <code>viewBox</code> with the smallest Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 0% 0%;</code>.</p>
</dd>
<dt>xMinYMid</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code> of the element’s <code>viewBox</code> with the smallest X value of the viewport.</p>
<p>Align the midpoint Y value of the element’s <code>viewBox</code> with the midpoint Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 0% 50%;</code>.</p>
</dd>
<dt>xMinYMax</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code> of the element’s <code>viewBox</code> with the smallest X value of the viewport.</p>
<p>Align the <code>min-y</code>+<code>height</code> of the element’s <code>viewBox</code> with the maximum Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 0% 100%;</code>.</p>
</dd>
<dt>xMidYMin</dt>
<dd>
Force uniform scaling.
<p>Align the midpoint X value of the element’s <code>viewBox</code> with the midpoint X value of the viewport.</p>
<p>Align the <code>min-y</code> of the element’s <code>viewBox</code> with the smallest Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 50% 0%;</code>.</p>
</dd>
<dt>xMidYMid (The default value)</dt>
<dd>
Force uniform scaling.
<p>Align the midpoint X value of the element’s <code>viewBox</code> with the midpoint X value of the viewport.</p>
<p>Align the midpoint Y value of the element’s <code>viewBox</code> with the midpoint Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 50% 50%;</code>.</p>
</dd>
<dt>xMidYMax</dt>
<dd>
Force uniform scaling.
<p>Align the midpoint X value of the element’s <code>viewBox</code> with the midpoint X value of the viewport.</p>
<p>Align the <code>min-y</code>+<code>height</code> of the element’s <code>viewBox</code> with the maximum Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 50% 100%;</code>.</p>
</dd>
<dt>xMaxYMin</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code>+<code>width</code> of the element’s <code>viewBox</code> with the maximum X value of the viewport.</p>
<p>Align the <code>min-y</code> of the element’s <code>viewBox</code> with the smallest Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 100% 0%;</code>.</p>
</dd>
<dt>xMaxYMid</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code>+<code>width</code> of the element’s <code>viewBox</code> with the maximum X value of the viewport.</p>
<p>Align the midpoint Y value of the element’s <code>viewBox</code> with the midpoint Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 100% 50%;</code>.</p>
</dd>
<dt>xMaxYMax</dt>
<dd>
Force uniform scaling.
<p>Align the <code>min-x</code>+<code>width</code> of the element’s <code>viewBox</code> with the maximum X value of the viewport.</p>
<p>Align the <code>min-y</code>+<code>height</code> of the element’s <code>viewBox</code> with the maximum Y value of the viewport.</p>
<p>Think of this as being similar to <code>background-position: 100% 100%;</code>.</p>
</dd>
</dl>
<p>So, using the <code>align</code> and <code>meetOrSlice</code> values of the <code>preserveAspectRatio</code> attribute, you can specify whether or not to scale the <code>viewBox</code> uniformly, how to align it inside the viewport, and whether or not it should be entirely visible inside the viewport.</p>
<p>Sometimes, and depending on the size of the <code>viewBox</code>, some values may have similar results. For example, in the <code>viewBox="0 0 200 300"</code> example from earlier, some alignments are identical using different <code>align</code> values. The value of <code>meetOrSlice</code> is set to <code>meet</code> in this case so that the entire <code>viewBox</code> is contained inside the viewport.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-meet-align-same.jpg" alt="" />
<figcaption>
The result of aligning the <code>viewBox</code> using different align values. <strong>The <code>meetOrSlice</code> value is set to <code>meet</code></strong>.
</figcaption>
</figure>
<p>If we were to change the <code>meetOrSlice</code> value to <code>slice</code>, we’d get different results for different values. Notice how the <code>viewBox</code> is stretched so that it covers the entire viewport. The x-axis is stretched so that the 200 units cover the viewport’s 800 units. In order for this to happen, and to maintain the aspect ratio of the viewbox, the y-axis gets “sliced off” at the bottom, but you can image it extending below the viewport’s height.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-slice-align-same.jpg" alt="" />
<figcaption>
The result of aligning the <code>viewBox</code> using different align values. <strong>The <code>meetOrSlice</code> value is set to <code>slice</code></strong>.
</figcaption>
</figure>
<p>Of course, different <code>viewBox</code> values will also look different from the 200x300 we’re using here. For the sake of brevity, I won’t get into more examples, and I’ll leave you to play with an interactive demo I created to help you better visualize how the <code>viewBox</code> and different <code>preserveAspectRatio</code> values work together when different values are used. You can check the interactive demo out by visiting the link in the next section.</p>
<p>But before we move to that, I just want to note that the mid-x, mid-y, max-x, and max-y values <em>change if the values of the <code><min-x></code> and <code><min-y></code> change</em>. You can play with the interactive demo and change these values to see how the axes and thus the alignment of the <code>viewBox</code> changes accordingly.</p>
<p>The following image shows the effect of using <code>viewBox = "100 0 200 300"</code> on the position of the alignment axes. We’re using the same example as earlier, but instead of having the <code><min-x></code> value be set to zero, we’re setting it to 100. You can set them to any number value you want. Notice how the min-x, mid-x, and max-x axes change. The <code>preserveAspectRatio</code> used here is the default <code>xMidYMid meet</code>, which means that the mid-* axes are aligned with the middle axes of the viewport.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-axes-changed-min-x-min-y.jpg" alt="" />
<figcaption>
The effect of changing the value of `min-x` on the position of the x-axes. <strong>The translucent blue area shows the area which is considered to be the <code>viewBox</code> area after changing the value of `min-x`</strong>.
</figcaption>
</figure>
<h3 class="deeplink" id="interactive-demo">The Interactive Demo</h3>
<p>The best way to understand how the viewport, <code>viewBox</code>, and different <code>preserveAspectRatio</code> values work and interact together is by visualizing them.</p>
<p>For that purpose, I created a simple interactive demo that allows you to change the values of these attributes and see the result of the new values live.</p>
<figure class="wide">
<img src="https://sarasoueidan.com/assets/images/viewbox-demo.gif" alt="Screenshot of the interactive demo." />
</figure>
<p><a class="btn" href="https://sarasoueidan.com/demos/interactive-svg-coordinate-system/index.html">Check the interactive demo out.</a></p>
<p>I hope you found this article useful in understanding the SVG viewport, <code>viewBox</code>, and <code>preserveAspectRatio</code> concepts. If you’d like to learn more about SVG coordinate systems, like nesting coordinate systems, establishing new ones, and transformations in SVG, stay tuned for the remaining parts of this series. You can subscribe to the RSS (link below) or follow me on Twitter to stay updated. Thank you very much for reading!</p>
<p class="note">
The SVG parrot illustration used is a freebie from <a href="http://freepik.com/">Freepik.com</a>.
</p>
<p>If you enjoyed this article you may also be interested in:</p>
<ul>
<li><a href="http://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg/">Structuring, Grouping, and Referencing in SVG – The `g`, `use`, `defs` and `symbol` Elements</a></li>
<li><a href="http://sarasoueidan.com/blog/css-svg-clipping">Clipping in CSS and SVG – The <code>clip-path</code> Property and `clipPath` Element</a></li>
</ul>
Clipping in CSS and SVG — The clip-path Property and <clipPath> Element
2014-07-08T00:00:00Z
https://sarasoueidan.com/blog/css-svg-clipping/
<p class="deck">CSS and SVG have a lot in common. A lot of the features that we have in CSS today were imported from SVG. One of these features is the <em><strong>Clipping</strong></em> operation. Both CSS and SVG allow us to "clip" elements into custom non-rectangular shapes. In this article we will go over the clipping techniques in both CSS and SVG, covering everything you need to know to get started.</p>
<p class="note warning">
Please note that the demos in this article may not work in your browser. You should check <a href="https://github.com/awgreenblatt/css-graphics">this compatibility table</a> out for more information.<strong> You don't <em>need</em> to view the live demos to follow up with the article.</strong> Not all clipping features are implemented and some features may be buggy. The purpose of this article is to go over how clipping works in CSS and SVG, and serves as a reference for when these features are fully implemented. I'm also not including any vendor prefixes in the code samples here, but they are included in the live demos.
</p>
<h3 class="deeplink" id="clipping">What is clipping?</h3>
<p>Clipping is a graphical operation that allows you to fully or partially hide portions of an element. <strong>The clipped element can be any container or graphics element.</strong> The portions of the element that are shown or hidden are determined by a <em>clipping path</em>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-illustration.svg" alt="" />
</figure>
<p>A <strong>clipping path</strong> defines a region where everything on the “inside” of this region is allowed to show through but everything on the outside is “clipped out” and does not appear on the canvas. This region is known as the <em>clipping region</em>. Any parts of the element that lie outside of a clipping region are not drawn. This includes any content, background, borders, text decoration, outline and visible scrolling mechanism of the element to which the clipping path is applied, and those of its descendants.</p>
<blockquote class="pull-quote">
The clipped element can be any container or graphics element.
</blockquote>
<p>A clipping path is conceptually equivalent to a custom viewport for the element it applies to. It influences what parts of the element are rendered on the screen, but it does <em>not</em> affect the element’s inherent geometry—the element will affect the flow around it as it normally would, and every other element on the page will still see and treat the element as if it were still rectangular, even if it’s clipped to a non-rectangular shape. If you want to change the way the content around the element flows and have it respond to the defined shape of the clip path, you can use the <a href="http://www.w3.org/TR/css-shapes/">CSS Shapes</a> properties. If you want to learn more about how to do that, you can check <a href="http://alistapart.com/article/css-shapes-101">the</a> <a href="http://sarasoueidan.com/blog/css-shapes/">articles</a> I wrote about this topic.</p>
<h3 class="deeplink" id="clip-path">Clipping in CSS – The <code>clip-path</code> Property</h3>
<p>The <code>clip-path</code> property is part of the CSS <a href="http://www.w3.org/TR/2014/WD-css-masking-1-20140213/">Masking Module</a>. The clipping operation has been a part of SVG since 2000, and has moved into the CSS Masking module so that it now allows clipping HTML elements as well as SVG elements.</p>
<p>The <code>clip-path</code> property is used to specify a clipping path that is to be applied to an element. Using <code>clip-path</code>, you can apply an SVG <code><clipPath></code> to an element by referencing that path in the property value. You can also define a clipping path using one of the <strong><a href="http://dev.w3.org/csswg/css-shapes-2/#ltbasic-shapegt">basic shapes</a></strong> defined in the CSS Shapes module. These shapes can be created using <strong>shape functions</strong>. The shape functions available are <code>polygon()</code>, <code>circle()</code>, <code>inset()</code> (used to define inset rectangles), and <code>ellipse()</code>.</p>
<p>Applying a clipping path to an element using the <code>clip-path</code> property is very simple and straightforward:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Referencing an SVG clipPath */</span></span><br /><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>#svgClipPathID<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* Using a CSS basic shape function */</span></span><br /><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>...<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* or one of the other shape functions */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>For example, if we were to define a polygonal clipping path using the <code>polygon()</code> function, and then apply it to an image, the code would look like the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">img</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>626px 463px<span class="token punctuation">,</span>765px 236px<span class="token punctuation">,</span>687px 31px<span class="token punctuation">,</span>271px 100px<span class="token punctuation">,</span>70px 10px<span class="token punctuation">,</span>49px 250px<span class="token punctuation">,</span>133px 406px<span class="token punctuation">,</span>374px 462px<span class="token punctuation">,</span>529px 393px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The result of applying the above line of CSS to an image would look like the following:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-example-1.png" alt="" />
</figure>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css/index.html" class="button">View Live Demo</a></p>
<p>The basic shape functions allow us to create a limited number of shapes; the most complex of these shapes is a polygon. If you want to use a more complex shape that looks like more than just a group of connected straight lines, you can use the SVG <code><clipPath></code> element. As the name <code><clipPath></code> implies, you can clip to any arbitrary path. This means that you can use the <code><path></code> element to create any arbitrary path and use that as a clipping path.</p>
<p>In our second example, we’re going to define and use an SVG <code>clipPath</code>. The code for the clip path looks like the following:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svgPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#FFFFFF<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.5794<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M215,100.3c97.8-32.6,90.5-71.9,336-77.6<br /><span class="highlight-line"> c92.4-2.1,98.1,81.6,121.8,116.4c101.7,149.9,53.5,155.9,14.7,178c-96.4,54.9,5.4,269-257,115.1c-57-33.5-203,46.3-263.7,20.1</span><br /> c-33.5-14.5-132.5-45.5-95-111.1C125.9,246.6,98.6,139.1,215,100.3z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>And this is how the clipping path looks like; it is just a simple path with no fill and a black stroke.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/the-svg-clippath.png" alt="" />
</figure>
<p>We’ll be talking more about SVG <code><clipPath></code> in the next section. But for now, we’re just going to reference it and apply it to the image we have.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">img</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>#svgPath<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And the result would look like the following:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-example-2.png" alt="" />
</figure>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css-svg-clippath/index.html" class="button">View Live Demo</a></p>
<p>Indeed, the <code><clipPath></code> element can contain any number of basic shapes (<code><rect></code>, <code><circle></code>, etc.), <code><path></code> elements, or even <code><text></code> elements.</p>
<p>If you specify a piece of <code><text></code> inside a <code><clipPath></code>, that text will be used as a clipping path. Whatever’s under the text will be visible “through” it, and anything outside the text area will not be rendered.</p>
<p>Note here that you can clip <em>anything</em> to the text. This opens a door for a lot of possibilities and effects. You can use animated images (such as GIFs) or even videos, and then clip them to some text of your choice. The sky is the limit here.</p>
<p>The following is an example of a piece of text used as a clipping path.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svgTextPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>text</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span> <span class="token attr-name">textLength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800px<span class="token punctuation">"</span></span> <span class="token attr-name">lengthAdjust</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>spacing<span class="token punctuation">"</span></span> <span class="token attr-name">font-family</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Vollkorn<span class="token punctuation">"</span></span> <span class="token attr-name">font-size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>230px<span class="token punctuation">"</span></span> <span class="token attr-name">font-weight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>700<span class="token punctuation">"</span></span> <span class="token attr-name">font-style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>italic<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Blossom <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>text</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The cool thing about SVG <code><text></code> is that it can be displayed using a custom font, just like HTML text can. In this example I’m using the <a href="http://www.google.com/fonts/specimen/Vollkorn">Vollkorn font</a> from Google Web Fonts. I’ve set the width of the text to be the same as the width of the image, using the <code>textLength</code> attribute, and positioned the text using the <code>x</code> and <code>y</code> coordinates. Note here that the <code>x</code> and <code>y</code> coordinates determine the position of the bottom left corner of the text (where the bottom stands for the baseline of the text).</p>
<p>The result of applying the above text clip path to the image looks like so:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-example-3.png" alt="" />
</figure>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css-svg-clippath-text/index.html" class="button">View Live Demo</a></p>
<p>And as we mentioned, you can also use multiple basic shapes inside <code><clipPath></code>. We’ll dig into <code><clipPath></code> and its contents in the next section, so, for now, we’ll keep it simple. In this example I’m using multiple <code><circle></code>s, each with a different size and position.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svgPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>193.949<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>235<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>74.576<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>426.576<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>108.305<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>47.034<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>346.915<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>255.763<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>43.644<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>255.39<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>82.882<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35.17<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- more circles... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The image will show through these circles combined, but will not be rendered outside them.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-example-4.png" alt="" />
</figure>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css-svg-clippath-multiple-shapes/index.html" class="button">View Live Demo</a></p>
<p>As we mentioned at the beginning of this article, you can apply clip paths using the <code>clip-path</code> property to SVG elements too. In all of the above examples, the clipping paths were applied to an HTML <code><img></code>. In the following example, a clipping path is applied to the root <code><svg></code> element. The same cherry blossoms image we used above is now part of the SVG, referenced using the <code><image></code> element.</p>
<p>The <code><image></code> element in SVG is used to include a graphic that can be either an entire SVG or a raster image. If it’s an SVG you’re referencing in <code><image></code>, the <code>width</code> and <code>height</code> attributes will be used to establish the viewport of that SVG. If you’re referencing a raster image (which is what we’re doing here), the image will be scaled to fit in the specified <code>width</code> and <code>height</code>. So I made sure the aspect ratio of the <code>width</code> and <code>height</code> attribute match the aspect ratio of the image I’m using, to prevent it from being distorted.</p>
<p>When you create an <code><svg></code> document, you establish its viewport by specifying the width
and height of the <code><svg></code> element. Anything drawn outside the limits of the viewport will be clipped out and will not be displayed. You can establish a new custom viewport of your own with the <code><clipPath></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>image</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flowers.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>theSVGPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>108<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>121.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>25.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>55<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>455<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>192.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>9.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>60<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>484<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>271.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>44.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>63<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>416<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>349.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>25.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>208<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>447<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>574.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>44.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>60<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>446<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>644.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>9.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>68<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>471<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>736.5<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>18.5<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>49<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>462<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using <code>clip-path</code>, we’re going to apply the <code>clipPath</code> to the <code><svg></code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">svg</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>#theSVGPath<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<figure>
<img src="https://sarasoueidan.com/assets/images/clipping-example-5.png" alt="" />
</figure>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/svg-image-clipped-with-css/index.html" class="button">View Live Demo</a></p>
<p>More examples applying a clipping path to an SVG element in the <code><clipPath></code> section below.</p>
<h4 class="deeplink" id="reference-box">A Clipping Path's Reference Box</h4>
<p>In addition to the clipping path itself, you can define a <em>reference box</em> in the <code>clip-path</code> property when the clipping path applied is a <code><basic-shape></code>; that is, a clipping path created using one of the basic shape functions. The reference box is hence only specified for CSS shapes used as clip paths, not for SVG <code><clipPath></code>s. For an SVG <code><clipPath></code>, the reference box is the border box of an HTML element.</p>
<p>So a reference box is specified for a <code><basic-shape></code> clip path. If the element being clipped is an HTML element, the reference box can be one of the four basic box model boxes: <code>margin-box</code>, <code>border-box</code>, <code>padding-box</code>, or <code>content-box</code>. Each of these is self-explanatory.</p>
<p>If the <code><basic-shape></code> clip path is applied to an SVG element, the reference box can be set to one of three keyword values:</p>
<ul>
<li>fill-box – uses the object bounding box as the reference.</li>
<li>stroke-box – uses the stroke bounding box as the reference.</li>
<li>view-box – uses the uses the nearest SVG viewport as the reference box if no <code>viewBox</code> is specified. If a <code>viewBox</code> is indeed specified, then the coordinate system is established by the origin and dimensions specified by the <code>viewBox</code>.</li>
</ul>
<p>If you set any of the CSS box model boxes as a reference box for an SVG element, the <code>fill-box</code> will be used. And if you use one of the SVG reference boxes on an HTML element, the <code>border-box</code> will be used.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>...<span class="token punctuation">)</span> padding-box<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>If <em>only</em> a reference box is specified in the <code>clip-path</code> property—that is, no basic shape is defined—the browser uses the edges of the specified box, including any corner shaping (e.g. defined by the <code>border-radius</code> property), as clipping path.</p>
<p>For example, using the following snippet, the element will be clipped to the rounded corners specified by <code>border-radius</code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.el</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> 25%<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p class="note">Note that at the time of writing of this article, specifying a reference box in the `clip-path` property doesn't work in Webkit because it's not yet implemented.</p>
<h4 class="deeplink" id="clip-path-notes"><code>clip-path</code> Notes: Stacking Contexts, Pointer Events and Animations</h4>
<p>It is important to know that any value other than the default <code>none</code> for the <code>clip-path</code> property results in the creation of a stacking context on the element the same way the <code>opacity</code> property does.</p>
<blockquote class="pull-quote">
Any value other than the default `none` for the `clip-path` property results in the creation of a stacking context on the element.
</blockquote>
<p>Furthermore, according to the Masking specification, <strong>pointer events must not be dispatched on the clipped-out (non-visible) regions of a shape</strong>. This means that the element should not respond to pointer events outside the remaining visible area.</p>
<p>A clipping path can also be animated. If the clipping path used is an SVG <code><clipPath></code>, it can be animated by including an animation inside it (See next section for details). If the cipping path is a basic shape created using a basic shape function, it can be animated using CSS animations and transitions. For details on how to animate a shape created using a shape function, check out the <a href="http://sarasoueidan.com/blog/animating-css-shapes">Animating CSS Shapes with CSS Animations and Transitions</a> article I wrote a while back.</p>
<h3 class="deeplink" id="svg-clippath-element">Clipping in SVG – The <code><clipPath></clipPath></code> Element</h3>
<p>In SVG, the <code>clipPath</code> element is used to define a clipping path to clip elements. If you don’t want to use CSS to apply the clipping path to an element, you can do it in SVG using the <code>clip-path</code> presentation attribute.</p>
<p class="note">
Have you seen/read my "Styling and Animating Scalable Vector Graphics with CSS" slides? If not, you may want to have a look at them for more information about SVG presentation attributes and CSS properties used to style SVG elements. You can check them out <a href="https://docs.google.com/presentation/d/1Iuvf3saPCJepVJBDNNDSmSsA0_rwtRYehSmmSSLYFVQ/pub?start=false&loop=false&delayms=3000#slide=id.p">here</a>.
</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myClippingPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- the element you want to apply the clipPath to can be any SVG element --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-graphic<span class="token punctuation">"</span></span> <span class="token attr-name">clip-path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#myClippingPath)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<h4 class="deeplink" id="contents-of-clippath">Contents Of a <code><clipPath></clipPath></code></h4>
<p>We mentioned earlier that an SVG <code>clipPath</code> can contain any number of basic shapes, arbitrary <code><path></code>s, and/or <code><text></code> elements. It can even contain more than that, and this is where it can get interesting.</p>
<p>The <code><clipPath></code> content can be descriptive (<code><title></code>, <code><desc></code>, <code><metadata></code>). It can also be a shape (<code><circle></code>, <code><ellipse></code>, <code><line></code>, <code><path></code>, <code><polygon></code>, <code><polyline></code>, <code><rect></code>) or a <code><text></code>. A <code><clipPath></code> can also contain a <code><use></code> element or a <code><script></code>. Note that <code><use></code> in <code><clipPath></code> can only reference the simple SVG shapes mentioned above—it cannot be used to reference groups inside <code><clipPath></code>, for example; that simply won’t work.</p>
<p>And last but not least, a <code><clipPath></code> can contain an <strong>animation</strong> using <code><animate></code>, <code><animateColor></code>, <code><animateMotion></code>, <code><animateTransform></code>, or <code><set></code>. This opens a door for a lot of creativity, as you can imagine.</p>
<p>To demonstrate, I’m just going to add a simple animation to the demo using multiple <code><circle></code>s as a clipping path. Every <code><circle></code> will get an animation. Because I want to keep it simple, I’m just gonna use the same animation on all of the circles. You can create fancier effects using different effects and playing with animation delays, of course. But, since this is a 101 article, I’m gonna stay on the simple side. The code with the animations look like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svgPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>animate</span><br /><span class="highlight-line"> <span class="token attr-name">attributeName</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">attributeType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>XML<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">from</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>250<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">begin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0s<span class="token punctuation">"</span></span> <span class="token attr-name">dur</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3s<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>freeze<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">repeatCount</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>indefinite<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>circle</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>193.949<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>235<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>74.576<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>animate</span><br /><span class="highlight-line"> <span class="token attr-name">attributeName</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">attributeType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>XML<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">from</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>250<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">begin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0s<span class="token punctuation">"</span></span> <span class="token attr-name">dur</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3s<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>freeze<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">repeatCount</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>indefinite<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>circle</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>The animation specified for each circle will animate the size of the circle—more specifically, its radius (<code>r</code>)—over the course of three seconds, from <code>0</code> to <code>250</code> pixels. I’ve also set the animation to repeat indefinitely.</p>
<p>Click on the following button to view the live demo. But before you do, note that there is a bug (see bug details <a href="https://code.google.com/p/chromium/issues/detail?id=391604">here</a>), so the demo may not work for you if you’re on Chrome or Safari. For now, I recommend using Firefox to see the working live demo, until the bug has been fixed.</p>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css-svg-clippath-animated/index.html" class="button">View Animated clipPath Demo Live</a></p>
<p>Note that the content of a <code><clipPath></code> also cannot involve groups (<code><g></code>s). For example, if we were to add a group element to the demo that uses multiple circles as a clipping path, the demo will no longer work—the clipping path will no longer be applied to the image.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>clipPath</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svgPath<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment"><!-- WILL NOT WORK --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span><span class="token punctuation">></span></span> <span class="token comment"><!-- WILL NOT WORK --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>193.949<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>235<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>74.576<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000000<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-miterlimit</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>426.576<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>108.305<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>47.034<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>clipPath</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<h4 class="deeplink" id="clippathunits">The <code>clipPathUnits</code> Attribute</h4>
<p>The <code><clipPath></code> element can have several attributes, including <code>id</code>, <code>class</code>, <code>transform</code>s, and <a href="http://www.w3.org/TR/2011/REC-SVG11-20110816/intro.html#TermPresentationAttribute">presentation attributes</a> like <code>fill</code> and <code>stroke</code>, among <a href="http://www.w3.org/TR/2011/REC-SVG11-20110816/styling.html#SVGStylingProperties">many others</a>. But the one attribute that stands out, and that is particularly useful, is the <code>clipPathUnits</code> attribute.</p>
<blockquote class="pull-quote">
The <code>clipPathUnits</code> attribute is used to specify a coordinate system for the contents of the <code><clipPath></clipPath></code>.
</blockquote>
<p>The <code>clipPathUnits</code> attribute is used to specify a coordinate system for the contents of the <code><clipPath></code>. It takes one of two values: <code>objectBoundingBox</code> or <code>userSpaceOnUse</code>. The default value is <code>userSpaceOnUse</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">clipPathUnits = "userSpaceOnUse | objectBoundingBox"</span></code></pre>
<dl>
<dt>userSpaceOnUse</dt>
<dd>
The contents of the <code>clipPath</code> represent values in the current <em><strong>user coordinate system</strong></em> in place at the time when the <code>clipPath</code> element is referenced (i.e., the user coordinate system for the element referencing the <code>clipPath</code> element via the <code>clip-path</code> property).
<p>
The current <strong>user coordinate system</strong> (a.k.a <em><strong>local coordinate system</strong></em>) is the coordinate system that is currently active and which is used to define how coordinates and lengths are located and computed. The user coordinate system for an HTML element with an associated CSS box model is different from that of an SVG element with no such box model.
</p>
<p>
For elements that have an associated CSS layout box, the current user coordinate system has its origin at the top left corner of a reference box and one unit equals one CSS pixel. The viewport for resolving percentage values is defined by the width and height of the reference box. I'm sure you're already familiar with this. So if you have a <code><clipPath></clipPath></code> containing a <code><circle></circle></code> whose center is positioned at <code>cx = "100"</code> and <code>cy = "100"</code>, the center will be positioned 100 pixels to the left and 100 pixels down inside the boundaries of the reference box.
</p>
<p>
If the element is an SVG element and thus does not have an associated CSS layout box, the current user coordinate system has its origin at the top left corner of the element's <strong>nearest viewport</strong>. In most cases, the nearest viewport is the viewport established by the width and height of the closest <code><svg></svg></code> ancestor. If you're not nesting <code><svg></svg></code>s, it's simply the viewport you establish on the root <code><svg></svg></code>.
</p>
<p>
Note that the coordinate system on an SVG element can be modified using the <code>viewBox</code> attribute, among other attributes which may contribute to changing the coordinate system. However, that's outside the scope of this article. So in this article I'm going to work under the assumption that no <code>viewBox</code> is modified, and hence the browser will use the default coordinate system with the origin at the top left corner, and dimensions equal to the dimensions of the <code><svg></svg></code>.
</p>
</dd>
<dt>objectBoundingBox</dt>
<dd>
The coordinate system has its origin at the top left corner of the <strong><em>bounding box</em></strong> of the element to which the clipping path applies to and the same width and height of this bounding box. A bounding box is the object bounding box for all SVG elements (it contains only an element's geometric shape) and the border box for HTML elements with an associated box model.
<p>
This value is particularly useful for SVG elements because it allows you to apply the clip path to the boundaries of the element itself, not the coordinate system on use. To demonstrate, here is an image showing the result of applying the clip path to an image inside an SVG canvas using <code>userSpaceOnUse</code> versus <code>objectBoundingBox</code>. The grey border represents the border of the <code><svg></svg></code> element where the viewport is set. For the image on the right, I've added a grey border around the clipped image just to show the boundaries of the bounding box.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clippathunits.png" alt="" />
<figcaption>The result of applying the <code>clipPath</code> to an image inside the SVG canvas using <code>userSpaceOnUse</code> (left) and <code>objectBoundingBox</code> (right). </figcaption>
</figure>
<p>
In the image on the left, the clipping path is positioned in the coordinate system established on the viewport of the SVG. When using <code>objectBoundingBox</code>, the bounding box of the image itself is used as the coordinate system of the clipping path.
</p>
<p>
One important thing to note here is that <strong>when you use the <code>objectBoundingBox</code> value, the coordinates specified for the contents of the <code><clipPath></clipPath></code> must be in the range [0, 1]</strong>—the coordinate system becomes a unit system, and the coordinates of the shapes inside a <code>clipPath</code> have to be fractions in that range.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/clippathunits-system.jpg" alt="" />
<figcaption>
The coordinate system used for the <code>objectBoundingBox</code> value on the right, versus that used for the <code>userSpaceOnuse</code> on the left.
</figcaption>
</figure>
<p>
For example, if the clip path being applied to an element contains a circle positioned so that its center lies at the center of the clipped element:
</p>
```html
<clipPath>
<circle cx="350" cy="350" r="300"></circle>
</clipPath>
```
<p>
the circle position (and radius) would be expressed in fractions like so:
</p>
```html
<clipPath clipPathUnits="objectBoundingBox">
<circle cx=".5" cy=".5" r=".45"></circle>
</clipPath>
```
<p>
The fractions are like percentage values in this case.
</p>
</dd>
</dl>
<h4 class="deeplink" id="-svg-clippath-notes"><code><clipPath></clipPath></code> Notes</h4>
<p><code>clipPath</code> elements are never rendered directly; their only usage is as something that can be referenced using the <code>clip-path</code> property. The <code>display</code> property does not apply to the <code>clipPath</code> element; thus, <code>clipPath</code> elements are not directly rendered even if the <code>display</code> property is set to a value other than <code>none</code>, and <code>clipPath</code> elements are available for referencing even when the <code>display</code> property on the <code>clipPath</code> element or any of its ancestors is set to <code>none</code>.</p>
<p>Remember what we said earlier about pointer events when an HTML element is clipped? The same standard behavior is defined in the SVG Clipping and Masking specification: By default, pointer-events must not be dispatched on the clipped (non-visible) regions of an SVG element. The spec then mentions that <q>later versions of SVG may define new properties to enable fine-grained control over the interactions between hit testing and clipping</q>.</p>
<p>Firefox implements the same non-standard behavior we mentioned before—areas outside the clipping regions do not respond to pointer events.</p>
<p>Even though Chrome implements the standard behavior for the <code>clip-path</code> property on HTML elements, when you apply a <code><clipPath></code> to an SVG element, the behavior is the same as the one implemented in Firefox—only the visible areas respond to pointer events. I’m not sure if this is a feature or a bug.</p>
<p>In the following example, an SVG <code><clipPath></code> is applied to an SVG <code><image></code>. The clip path is similar to the one we used before, where the image is clipped by a number of rectangles. The image becomes translucent when you hover over it.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">image</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>#svgPath<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">image:hover</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> .5<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><a href="https://sarasoueidan.com/demos/css-svg-clipping/svg-img-clipped-pointer-events/index.html" class="button">Try The Demo Out Live</a></p>
<p>Also, note that an empty clipping path will completely clip away the element that had the <code>clip-path</code> property applied.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>Clipping is one of those graphical operations that allow us to create irregular shapes in an otherwise rectangular web page. Indeed, clipping is a perfect companion to CSS shapes. If you’ve read any of my <a href="http://alistapart.com/article/css-shapes-101">previous</a> <a href="http://sarasoueidan.com/blog/css-shapes/">articles</a> about CSS Shapes, then you already know that the <code>clip-path</code> property can be an indispensable companion to CSS Shapes in some use cases. And once CSS Shapes properties can <a href="http://dev.w3.org/csswg/css-shapes-2/#referencing-svg-shapes">reference SVG paths</a> (<a href="http://dev.w3.org/csswg/css-shapes-2/">CSS Shapes Module Level 2</a>), in addition to the basic CSS shapes, the combination of Shapes and Clipping will allow us to create visually compelling designs that break the norms of the rectangle.</p>
<p>I hope you found this article useful. Thank you for reading! --></p>
Structuring, Grouping, and Referencing in SVG — The<g>, <use>, <defs> and <symbol> Elements
2014-07-03T00:00:00Z
https://sarasoueidan.com/blog/structuring-grouping-referencing-in-svg/
<p class="deck">SVG comes with its own ways for structuring a document by means of certain SVG elements that allow us to define, group, and reference objects within the document. These elements make reusing elements easy, while maintaining clean and readable code. In this article we'll go over these elements, highlighting the difference between them and the advantages of each one.</p>
<h3 class="deeplink" id="the-g-element">Grouping with the <code><g></g></code> element</h3>
<p>The ‘g’ in <code><g></code> stands for ‘group’. The group element is used for logically grouping together sets of related graphical elements. In terms of graphics editors, such as Adobe Illustrator, the <code><g></code> element serves a similar functionality as the <em>Group Objects</em> function. You can also think of a group as being similar to the concept of a <em>layer</em> in a graphics editor, since a layer is also a grouping of elements.</p>
<blockquote class="pull-quote">
The group element is used for logically grouping together sets of related graphical elements.
</blockquote>
<p>The <code><g></code> element groups all of its descendants into one group. It usually has an <code>id</code> attribute to give that group a name. Any styles you apply to the <code><g></code> element will also be applied to all of its descendants. This makes it easy to add styles, transformations, interactivity, and even animations to entire groups of objects.</p>
<p>For example, the following is an SVG bird. The bird is made up of several shapes such as circles and paths.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/grouping-bird.svg" alt="" />
</figure>
<p>If you wanted to move the entire bird from one place to another in Illustrator, you will also want to group the elements together so that you wouldn’t have to select each and every one of them every time you wanted to do so.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/grouping-in-illustrator.png" alt="" />
</figure>
<p>Grouping in SVG using the <code><g></code> element works the same way. In this example we’ve grouped the elements of the body together, the elements of the head together, and then we grouped the two groups into one group with an <code>id</code> of <code>bird</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1144.12px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>400px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 572.06 200<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="highlight-line"> <span class="token selector">svg</span><span class="token punctuation">{</span><span class="token property">background-color</span><span class="token punctuation">:</span>white<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#wing</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#81CCAA<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#body</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#B8E4C2<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#pupil</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#1F2600<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#beak</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#F69C0D<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">.eye-ball</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#F6FDC4<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M48.42,78.11c0-17.45,14.14-31.58,31.59-31.58s31.59,14.14,31.59,31.58c0,17.44-14.14,31.59-31.59,31.59<br /> S48.42,95.56,48.42,78.11<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M109.19,69.88c0,0-8.5-27.33-42.51-18.53c-34.02,8.81-20.65,91.11,45.25,84.73<br /> c40.39-3.65,48.59-24.6,48.59-24.6S124.68,106.02,109.19,69.88<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wing<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M105.78,75.09c4.56,0,8.84,1.13,12.62,3.11c0,0,0.01-0.01,0.01-0.01l36.23,12.38c0,0-13.78,30.81-41.96,38.09<br /><span class="highlight-line"> c-1.51,0.39-2.82,0.59-3.99,0.62c-0.96,0.1-1.92,0.16-2.9,0.16c-15.01,0-27.17-12.17-27.17-27.17</span><br /> C78.61,87.26,90.78,75.09,105.78,75.09<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>head<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>beak<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M50.43,68.52c0,0-8.81,2.58-10.93,4.86l9.12,9.87C48.61,83.24,48.76,74.28,50.43,68.52<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eye-ball<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M60.53,71.68c0-6.33,5.13-11.46,11.46-11.46c6.33,0,11.46,5.13,11.46,11.46c0,6.33-5.13,11.46-11.46,11.46<br /> C65.66,83.14,60.53,78.01,60.53,71.68<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pupil<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M64.45,71.68c0-4.16,3.38-7.53,7.54-7.53c4.16,0,7.53,3.37,7.53,7.53c0,4.16-3.37,7.53-7.53,7.53<br /> C67.82,79.22,64.45,75.84,64.45,71.68<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eye-ball<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M72.39,74.39c0-2.73,2.22-4.95,4.95-4.95c2.73,0,4.95,2.21,4.95,4.95c0,2.74-2.22,4.95-4.95,4.95<br /> C74.6,79.34,72.39,77.13,72.39,74.39<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you were to change the fill color of the <code>#body</code> group, the fill color of all the elements inside the group will change to the color you specify. This is very convenient.</p>
<p>Grouping elements is very useful, not just for organizational and structural purposes. It’s particularly useful for when you want to add interactivity or transformations to an SVG graphic that is made up of several items. You can associate those items together in a group and then define transformations to move, scale, or rotate all the items together so that their spatial relations to one another are maintained.</p>
<p>If you wanted to scale the entire bird up and make it twice its size, you will be able to do it with one line of CSS if all the elements are grouped together.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">#bird</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Grouping makes interactivity, in particular, more convenient. You can attach mouse events to the entire bird and have it respond to the events as a whole group, instead of having to apply the same interactions and/or transformations to every element in that group.</p>
<p>The <code><g></code> element has one more important and great feature: it can have its own <code><title></code> and <code><desc></code> tags that help make it more accessible to screen readers, and overall make the code more readable to humans as well. For example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bird<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Bird<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>desc</span><span class="token punctuation">></span></span>An image of a cute little green bird with an orange beak.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>desc</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span></code></pre>
<h3 class="deeplink" id="the-use-element"> Reusing elements with the <code><use></use></code> element</h3>
<p>Often times, there are elements that are repeated in a graphic. If you’re working in Illustrator and you want to repeat an element somewhere in the graphic, you would copy the element and then paste it and use it wherever it is you want to use it. Copying and then pasting an existing element is more convenient than having to recreate that element from scratch.</p>
<p>The <code><use></code> element lets you reuse existing elements, giving you a similar functionality to the copy-paste functionality in a graphics editor. It can be used to reuse a single element, or a group of element defined with the <code><g></code> element.</p>
<blockquote class="pull-quote">
The <code><use></use></code> element lets you reuse existing elements, giving you a similar functionality to the copy-paste functionality in a graphics editor.
</blockquote>
<p>The <code><use></code> element takes <code>x</code>, <code>y</code>, <code>height</code>, and <code>width</code> attributes, and it references other content using the <code>xlink:href</code> attribute. So if you have defined a group somewhere with a given <code>id</code>, and you want to reuse it somewhere else, you give its URI in an <code>xlink:href</code>
attribute, and specify the <code>x</code> and <code>y</code> location where the group’s <code>(0, 0)</code> point should be moved to.</p>
<p>For example, if we were to create another bird in our SVG canvas, we could reuse the existing bird like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#bird<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Note that you can reference any SVG element inside the <code>xlink:href</code> attribute, even if that element is in an external file. The referenced element or group does not have to exist in the same file. This is great for organizing files (for example you could have a file for reusable components) and for caching the used files. If the bird in our example were created in a seperate file called <code>animals.svg</code> for example, we could have referenced it like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/animals.svg#bird<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>However, referencing external SVG in <code><use></code> doesn’t work in most versions of IE (up to IE 11). I recommend you read <a href="http://css-tricks.com/svg-use-external-source/">this article</a> by Chris Coyier for details and fallback mechanisms.</p>
<p>Now, those of you with a sharp bird’s eye (pun not intended), may have noticed that I said that the <code>x</code> and <code>y</code> attributes of <code><use></code> specify the location where the group’s upper left corner should be <strong>moved to</strong>. Moving an element means that you’re starting from its current position and then changing it to another one. Had I said “should be positioned at”, it would have implied that the element will be positioned according to the coordinate system in use on the entire canvas, right?</p>
<p>As it turns out, the <code>x</code> and <code>y</code> coordinates are <strong>a shorthand for translating an element using the <code>transform</code> attribute</strong>. More specifically, the above <code><use></code> use is equivalent to:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#bird<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>translate(100, 100)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<figure>
<img src="https://sarasoueidan.com/assets/images/bird-reuse.jpg" alt="" />
</figure>
<p>This fact means that <strong>the position of the new reused element is relative to the position of the original element that we’re reusing</strong>. This isn’t always optimal and can have some drawbacks.</p>
<blockquote class="pull-quote">
The <code>x</code> and <code>y</code> coordinates are a shorthand for translating an element using the <code>transform</code> attribute.
</blockquote>
<p>Another drawback of the <code><use></code> element is that the “copies” of the original element will have the exact same styles as the original element. Whenever you apply any style or transformation changes to the <code>#bird</code> group in the above example, all the copies of the bird will get the same styles and transformations.</p>
<p>You can <code>use</code> an element and apply an independent transformation to it. For example, the following line will reuse the bird, but it will make it half its original size using a scale transformation:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#bird<span class="token punctuation">"</span></span> <span class="token attr-name">transform</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>scale(0.5)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>However, you <strong>cannot override the styles of the original element (such as fill and strokes) on the copy</strong>. This means that if you want to create multiple birds or multiple icons (if you’re working with icons) and you want every icon to have a different color, this won’t be possible with the <code><use></code> element (unless the original element is defined inside a <code><defs></code> element without these styles applied to it. See next section for more information).</p>
<p>The <code><use></code> element allows you to reuse an element <strong>that is already rendered on the canvas</strong>. But what if you want to define an element <em>without</em> displaying it, and then draw it at specific positions when you need or want to? This is where the <code><defs></code> element comes in.</p>
<h3 class="deeplink" id="the-defs-element">Reusing Stored elements with the <code><defs></defs></code> element</h3>
<p>The <code><defs></code> element can be used to <strong>store</strong> content that will not be directly displayed. In other words, the <code><defs></code> element is used to <em>define</em> elements without directly rendering them. This stored hidden content can then be referenced and displayed by other SVG elements,
which makes it ideal for things such as patterns that contain reusable graphics.</p>
<blockquote class="pull-quote">
The <code><defs></defs></code> element is used to <em>define</em> elements without directly rendering them [..] and that element will serve as a <strong>template</strong> for future use.
</blockquote>
<p>So, using <code><defs></code> we can define an element that we want to use. This element can be anything, ranging from a group of elements like the bird we saw earlier, to a clipping path, mask, or a linear gradient. Basically, anything that we want to define and store away for later use can be defined inside the <code><defs></code> element, and that element will serve as a <strong>template</strong> for future use, or as a tool that is available for use whenever needed. The template is never rendered, only instances of it are displayed.</p>
<p>The following is an example defining an SVG gradient, and then uses it as a fill color for a simple SVG rectangle:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>linearGradient</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>gradient<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>stop</span> <span class="token attr-name">offset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0%<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">stop-color</span><span class="token punctuation">:</span> deepPink</span><span class="token punctuation">"</span></span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>stop</span> <span class="token attr-name">offset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">stop-color</span><span class="token punctuation">:</span> #009966</span><span class="token punctuation">"</span></span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>linearGradient</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#eee<span class="token punctuation">"</span></span> <span class="token attr-name">stroke-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url(#gradient)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Defining the linear gradient inside the <code><defs></code> element like that ensures that the gradient will not be rendered unless it is referenced somewhere it is needed.</p>
<p>In the previous section we mentioned two drawbacks of the <code><use></code> element:</p>
<ul>
<li>The position of the new element is relative to the position of the original element.</li>
<li>The styles of the original element cannot be overridden in the individual copies.</li>
</ul>
<p>That, in addition to the fact that the re<code>use</code>d element will be rendered on the canvas.</p>
<p>All of these drawbacks can be avoided using the <code><defs></code> element. Not only is the original element not rendered, but when you want to reuse an element inside <code><defs></code>, the position you specify for each instance will be set relative to the origin of the user coordinate system, not relative to the position of the original element (which makes sense considering the original element is a template that’s not even rendered on the canvas).</p>
<p>In this example we have a tree. The tree is made up of a bark and a group of leaves. The leaves are grouped into a group with <code>id="leaves"</code>, and then this group is grouped with the bark into the larger <code>tree</code> group.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500.79px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 500.79 200<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="highlight-line"> <span class="token selector">#leaves</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#8CC63F<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#bark</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#A27729<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tree<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bark<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M91.33,165.51c0,0,4.18-27.65,1.73-35.82l-18.55-25.03l3.01-2.74l17.45,19.87l1.91-37.6h4.44l1.83,24.53<br /> l15.26-16.35l3.27,4.36l-16.07,19.34c0,0-2.72,0-1.09,19.34c1.63,19.34,3,29.7,3,29.7L91.33,165.51z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>leaves<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>leaf<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M96.97,79.07c0,0-14.92,4.34-23.52-14.05c0,0,19.4-7.98,24.37,11.9c0,0-9.68-3.57-13.07-6.73<br /> C84.75,70.2,91.82,77.99,96.97,79.07z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>leaf<span class="token punctuation">"</span></span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M74.07,100.91c0,0-15.94-1.51-17.2-22.39c0,0,21.62-0.27,18.83,20.66c0,0-7.92-7.1-9.97-11.41<br /> C65.73,87.77,69.55,97.92,74.07,100.91z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>And this is how the tree looks like:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/defined-tree.jpg" alt="" />
</figure>
<p>If we were to wrap the <code>#tree</code> group in a <code><defs></code> element, the tree would no longer be rendered on the canvas.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500.79px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200px<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 500.79 200<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="highlight-line"> <span class="token selector">#leaves</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#8CC63F<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#bark</span><span class="token punctuation">{</span><span class="token property">fill</span><span class="token punctuation">:</span>#A27729<span class="token punctuation">;</span><span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tree<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>defs</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now the tree serves as a template for use. We can use it using the <code><use></code> element just like we would <code>use</code> any other element. The only difference in this case is that the <code>x</code> and <code>y</code> attributes are now set relative to the user coordinate system, not relative to the position of the used element.</p>
<p>For example, if we were to create three copies of the tree and position them on the SVG canvas, and assuming in this case that the user coordinate system matches the viewport’s height and width with the origin being on the top left corner of the SVG viewport, we’d get the following code with the following result:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><use <span class="token property">xlink</span><span class="token punctuation">:</span>href=<span class="token string">"#tree"</span> x=<span class="token string">"50"</span> y=<span class="token string">"100"</span> /></span><br /><span class="highlight-line"><use <span class="token property">xlink</span><span class="token punctuation">:</span>href=<span class="token string">"#tree"</span> x=<span class="token string">"200"</span> y=<span class="token string">"100"</span> /></span><br /><span class="highlight-line"><use <span class="token property">xlink</span><span class="token punctuation">:</span>href=<span class="token string">"#tree"</span> x=<span class="token string">"350"</span> y=<span class="token string">"100"</span> /></span></code></pre>
<figure>
<img src="https://sarasoueidan.com/assets/images/tree.svg" alt="" />
</figure>
<p>As you can see in the image above, each of the trees was positioned relative to the origin of the coordinate system which in this case is the upper left corner of the SVG. So the upper left corner of each tree is positioned at its own (<code>x</code>, <code>y</code>) position in the user coordinate system, independent of the other trees and independent of the tree template defined inside <code><defs></code>.</p>
<p>When you use <code><defs></code> to reuse an element, you can apply different styles and fill colors to each individual tree, <strong>as long as these styles are not defined on the original tree template</strong>. If the tree inside <code><defs></code> has styles applied to it, those styles still won’t be overridden by any new ones. So <code><defs></code> is great for creating minimal templates and then styling the copies as needed. Without <code><defs></code>, this wouldn’t be possible with <code><use></code> alone.</p>
<p>Note that elements inside the <code><defs></code> element are prevented from becoming part of the rendering tree just as if the <code>defs</code> element were a <code>g</code> element and the <code>display</code> property were set to <code>none</code>. However, that the descendants of a <code>defs</code> are always present in the source tree and thus can always be referenced by other elements; thus, the value of the <code>display</code> property on the <code>defs</code> element or any of its descendants does not prevent those elements from being referenced by other elements, even if it is set to <code>none</code>.</p>
<h3 class="deeplink" id="the-symbol-element">Grouping elements with the <code><symbol></symbol></code> element</h3>
<p>The <code><symbol></code> element is similar to the group element <code><g></code>—it provides a way to group elements together. However, it differs from the group element in two main things:</p>
<ul>
<li>The <code><symbol></code> element is not rendered. It is actually similar to <code><defs></code> in this manner. It is only displayed when it is <code>use</code>d.</li>
<li>A <code><symbol></code> element can have its own <code>viewBox</code> and <code>preserveAspectRatio</code> attributes. This allows it to fit into the viewport it is rendered into any way you want it to, instead of fitting in the default way.</li>
</ul>
<p><code><symbol></code> is then mostly suitable for <strong>defining</strong> reusable elements (or <em>symbols</em>). It also serves as a template that is <strong>instantiated</strong> using the <code><use></code> element. And having <code>viewBox</code> and <code>preserveAspectRatio</code> attributes, it can scale-to-fit within a rectangular viewport defined by the referencing <code><use></code> element. Note that <code>symbol</code> elements define new viewports whenever they are instanced by a <code>use</code> element.</p>
<blockquote class="pull-quote">
`symbol` elements define new viewports whenever they are instanced by a `use` element.
</blockquote>
<p>This feature is great because it allows you to define elements that are independent of the viewport they’re rendered into, hence allowing you to make sure the symbol you’re referencing will always display a certain way inside the viewport.</p>
<p>You need to be familiar with the way the <code>viewBox</code> works, and the values of the <code>preserveAspectratio</code> attribute to make the best use of this feature. Chris Coyier wrote <a href="http://css-tricks.com/svg-symbol-good-choice-icons/">an article</a> explaining why the <code><symbol></code> element can be a good choice for icons, and how to use it.</p>
<p class="note">I will also be writing an extensive article about the `viewport`, `viewBox`, and `preserveAspectRatio` attributes that explains how these attributes work together and how they can be used to control and scale graphics in SVG, so stay tuned for that if you're interested.</p>
<div class="update note">
<strong>UPDATE:</strong> Article is live here: <a href="http://sarasoueidan.com/blog/svg-coordinate-systems">Understanding SVG Coordinate Systems and Transformations (Part 1) – The viewport, <code>viewBox</code>, and <code>preserveAspectRatio</code></a>
</div>
<p>Note that the <code>display</code> property does not apply to the <code>symbol</code> element; thus, <code>symbol</code> elements are not directly rendered even if the <code>display</code> property is set to a value other than <code>none</code>, and <code>symbol</code> elements are available for referencing even when the <code>display</code> property on the <code>symbol</code> element or any of its ancestors is set to <code>none</code>.</p>
<h3 class="deeplink" id="wrapping-up">Wrapping up</h3>
<p>All of these elements are container structural elements in SVG that helps make reusing elements easier while keeping the code cleaner and more readable. And each of the elements we went over in this article has its own use cases. Now that you know what each one does and how they differ, you can decide for your own which one to use, depending on your needs. That said, don’t forget to <a href="https://www.sitepoint.com/tips-accessible-svg/">keep your SVGs accessible</a> at all times.</p>
<p>I hope you liked this article and found it useful. Thank you for reading!</p>
Everything You Need To Know About The CSS will-change Property
2014-06-10T00:00:00Z
https://sarasoueidan.com/blog/css-will-change-property/
<p class="deck">
What the title says! I wrote this article for the Opera Developers' blog, and it literally contains everything you need to know about the new CSS <code>will-change</code> property, including but not limited to: how to use it, when to use it, when <em>not</em> to use it, performance considerations, and more.
</p>
Moving Forward With CSS Shapes
2014-05-05T00:00:00Z
https://sarasoueidan.com/blog/moving-forward-with-css-shapes/
<p class="deck">
Following up with the CSS Shapes 101 article, I share a list of great resources to learn more about CSS Shapes, including tutorials, examples, and developer tools.
</p>
CSS Shapes 101
2014-04-29T00:00:00Z
https://sarasoueidan.com/blog/css-shapes-101/
<p class="deck">
In this article, you'll learn everything you need to know to get started using CSS Shapes today. We will cover all the prerequisites: establishing a coordinate system, conditions for elements to be eligible for shapes, and more, then moving on to the properties used, their values and how each one affects the shape created, with lots of visual explanations and examples.
</p>
CSS Regions Matter
2014-02-15T00:00:00Z
https://sarasoueidan.com/blog/css-regions-matter/
<p class="deck">
Whether or not CSS Regions can be used to create multi-column or responsive layouts, fact remains that, unlike Flexbox, Multi-Columns, and Grids, <strong>CSS Regions are not a layout feature</strong>—they're <strong>a fragmentation feature</strong> that allows us to control or change the flow of content across containers in a page, or across pages.
</p>
<p>Since CSS Regions are a <strong>fragmentation feature</strong>, they define <em>where</em> content <em>flows</em> on the screen—unlike Flexbox, floats, Grids, and other positioning features that define <em>how</em> content is <em>laid out</em> on a screen. Layout features position elements and containers; Regions can alter the flow of content that shows up in them.</p>
<p>In this article I want to go over some useful examples of use cases for CSS Regions, and highlight some of the challenges that we may face when using CSS Regions, and talk about possible solutions to common Regions problems.</p>
<div class="note">
<p><em>I wrote this article before <a href="http://news.cnet.com/8301-1023_3-57617840-93/reversing-course-google-rejects-adobe-web-publishing-tech/">the news</a> came out that Google decided to pull Regions out of Blink, which, in my opinion, is a big loss for the web community. So, even though the content of the article may go in vain, I think it's still worth sharing why I think CSS Regions mattered, and wish they weren't ditched like that. I hope you guys like it anyway.</em></p>
<p><em><strong>Disclaimer</strong> In no way is this article meant as an attack on Mr. Håkon Wium Lie’s <a href="http://alistapart.com/blog/post/css-regions-considered-harmful">article</a> about CSS Regions. Mr. Håkon’s article served as an incentive for me to dig deeper into CSS Regions, and during the process, I learned a lot about them, and about other future CSS features, and this article is the result of what I found out and my personal opinion regarding the usefulness of CSS Regions.</em></p>
</div>
<h3 class="deeplink" id="regions-problems-and-solutions">CSS Regions Problems and Possible Solutions</h3>
<p>CSS regions have two prominent “problems” today: non-semantic presentational elements, and the non-responsiveness of regions.</p>
<h4 class="deeplink" id="non-semantic-markup">The Problem Of Non-Semantic Markup, and Possible Solutions</h4>
<p>The reason we are using non-semantic elements to create Regions today is that other layout features, such as Grids, for example, have not yet been finalized and are not yet ready to be used. So, instead of using portions or fragments of a CSS Grid layout, for example, as regions, we’re currently stuck with defining regions using “normal” elements such as <code>div</code>s.</p>
<p>CSS Regions are designed so that they work <em>now</em>, with normal elements, and also work with future features, instead of waiting for things to be perfect. It is only a matter of time before Regions can be used with other layout features, and then a lot of the current Regions problems can be solved.</p>
<p>When Regions are used with other features, such as Grids and Flexbox, they allow us to use fragments of a layout as regions by simply applying the <code>flow-from</code> property to them. No empty elements will be needed to define regions, whatsoever.</p>
<p>To demonstrate how Regions can be defined without creating empty elements, I will show a code example provided to me by Adobe’s <a href="https://twitter.com/cataling">Catalin Grigoroscuta</a>. In the demo, the pseudo-element <code>::after</code> is used to define regions to flow content into. So we do not need to add any empty elements to the page to create our regions, and therefore the semantics of the content are not compromised.</p>
<p>This demo is a variation of <a href="http://css-tricks.com/content-folding/">an excellent use case for Regions that Chris Coyier came up with</a>: using regions to <strong>shuffle</strong> contents on a page, allowing us to reposition ads that are placed inside a sidebar on big screens, and place them in between the text content of an article on small screens, so that they don’t stack up down at the bottom of the article.</p>
<p>The layout would start with a sidebar having the ads inside it, much like most blogs and magazines do today.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/layout-wide.png" alt="Picture showing a typical layout with ads stacked in the sidebar" />
<figcaption>
The layout would contain the ads in a sidebar on big screens. <em><a href="http://css-tricks.com/content-folding/">Source</a></em>
</figcaption>
</figure>
<p>And then using CSS Regions, the content is “folded” on smaller screens, allowing ads to be placed between the text content of the article. Without CSS Regions, the ads would appear at the bottom of the article, way down at the bottom.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/folding.jpg" alt="Picture showing the effect of using regions to place ads inside content on small screens" />
<figcaption>
Without using CSS Regions, the ads would normally stack at the bottom of the content as in the left picture. Using CSS Regions, the content can be "folded" and ads placed in different spots inside the article. <em><a href="http://css-tricks.com/content-folding/">Source</a></em>
</figcaption>
</figure>
<p>Chris did it by introducing empty elements into the article where he wants the ads to appear on small screens. And then, using media queries, he flowed the ads from the sidebar into these elements using the CSS Regions properties <code>flow-from</code> and <code>flow-into</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Source: http://css-tricks.com/content-folding/ --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>main-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad-region-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- empty, ads flow into here as needed --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad-region-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- empty, ads flow into here as needed --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad-region-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- empty, ads flow into here as needed --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Fallback content in this flow region, probably clone #ad-source --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Source of flowing content, essentially hidden as an element --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad-source<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ads/1.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ads/2.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ads/3.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ads/4.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The CSS needed to define the flow of the ads into the regions is shown below.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* define the source and destination of the ads flow */</span></span><br /><span class="highlight-line"><span class="token selector">#ad-source</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-into</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-ms-flow-into</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">aside, [class^='ad-region']</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-from</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-ms-flow-from</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token comment">/* initially hide the regions (empty elements) */</span></span><br /><span class="highlight-line"><span class="token selector">[class^='ad-region']</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 155px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* show regions on small screens and hide the sidebar where the ads were on big screens */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 800px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">[class^='ad-region']</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">[class^='ad-region']:last-child</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span> <span class="token comment">/* I wish height auto worked */</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">aside</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can see that this use case for Regions is brilliant and very practical. But it has one problem: non-semantic empty elements.</p>
<p>A way to avoid using empty elements to define our regions would be to use the <code>::after</code> pseudo-element on each of the <code><article></article></code> elements, and use the pseudo-element as the region we flow the ads into. The following code shows how this can be achieved using just a few lines of CSS. No code bloating, no non-semantic elements.</p>
<p>First we create our markup, similar to the way Chris did it, but we leave out the empty elements.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos possimus tenetur aut natus error aperiam ipsam atque laboriosam quod accusamus velit deserunt non quo blanditiis officiis pariatur eveniet fugiat neque.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Possimus reiciendis eveniet ex suscipit nam ad voluptas cumque beatae qui maxime repellat quos consequuntur sapiente esse animi ea accusantium dicta perspiciatis?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem numquam tenetur natus ad unde quisquam illo fuga deserunt quibusdam accusamus provident officiis laboriosam hic nihil dolorum aperiam reprehenderit adipisci eveniet?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Ad 1<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80px<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>img</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Ad 2<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80px<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>img</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ad<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Ad 3<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300px<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80px<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>img</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span></code></pre>
<p>And then using Regions properties, we define our regions on the <code>::after</code> pseudo-element of each article.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/*<br /><span class="highlight-line">* Regions Code that makes the Folding Work</span><br /><span class="highlight-line">* 1) remove ads from sidebar and put them into a flow</span><br /><span class="highlight-line">* 2) "pour" the ads into the pseudo-element</span><br />*/</span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.ad</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-into</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span> <span class="token comment">/* 1 */</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector">article::after</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 5em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 1em 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-from</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span> <span class="token comment">/* 2 */</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">section</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This is a brilliant idea that solves the problem of non-semantic elements, and also shows how well Regions can work with other CSS features. You can see a fork of Catalin’s live demo <a href="http://codepen.io/SaraSoueidan/pen/ca006aa143bd27380bf1d99375299506">here</a>. And what’s even more awesome is that this technique and the code used for it are 100% reusable.</p>
<p>This example shows that the non-semantic elements that are now sometimes needed to define regions, will no longer be a problem when Regions are used in conjunction with other layouts features. Just like we defined regions on pseudo-elements, we would be able to define regions on fragments of the layout that are part of a Grid or Flexbox, for example, and the semantics wouldn’t be affected. Grids and other layout features would provide “slots” to use as regions, just like pseudo-elements can be used as regions.</p>
<h4 class="deeplink" id="responsiveness">The Responsiveness Of Regions Or Lack Thereof</h4>
<p>CSS Regions are not responsive by themselves, they need media queries to change. But then again, Flexbox does too, and so do CSS Grids, and that does not mean these features are not useful. Relying on Regions alone to fully “responsify” a layout is not a good idea, but that’s not surprising, considering that it’s not what Regions are made for.</p>
<p>The biggest challenge that we could face when making Regions responsive is fitting the content inside a Region that has a fixed height but different widths on different screen sizes. We all know how limiting that is. If we start mobile-first, the content will be too little for the element on bigger screens when it expands horizontally, and if we start desktop-first, the content will overflow the element on small screens. The thing about regions is that it does not define a way to generate new boxes to fit the overflowing content. This is a curse, but also a blessing, because it allows Regions to work well with any other specification that <em>can</em> generate boxes.</p>
<p>The repsonsiveness of CSS regions would no longer be a problem when they are combined with other features that can do what Regions can’t do, such as the <a href="http://dev.w3.org/csswg/css-overflow-3/">CSS Overflow Module</a>. If these technologies were to combine, then we would be able to create responsive layouts that have regions defined in them, without having to worry about content overflowing the regions.</p>
<p>The CSS Overflow Module is described in the specification as follows:</p>
<blockquote>
<p>This module contains the features of CSS relating to new mechanisms of overflow handling in visual media (e.g., screen or paper). In interactive media, it describes features that allow the overflow from a fixed size container to be handled by pagination (displaying one page at a time). It also describes features, applying to all visual media, <span style="text-decoration: underline">that allow the contents of an element to be spread across multiple fragments, allowing the contents to flow across multiple regions or to have different styles for different fragments</span>. (bold emphasis added by me)</p>
</blockquote>
<p>Considering how CSS Regions are meant to help us create and control fragments of a layout, this Overflow module looks like a great match for Regions. Together, they could allow us to make Regions responsive by “extending” a region. The Overflow module handles overflow into “fragments”—it duplicates the source element and flows content into it as many times as necessary to fit the whole content. So as the screen becomes smaller, the region can be “extended” to fit its content without having to overflow that content into another region or element. Extending a region would be as simple as defining <code>overflow: fragments;</code> on it, and we would end up with a solution to the fixed region height problem. That sounds really great, doesn’t it?</p>
<p>There is also another proposal from Adobe—<a href="http://adobe.github.io/css-pseudo-elements/">multiple pseudo-elements</a>—which allows the creation of multiple <code>::before</code> and <code>::after</code> pseudo-elements on an element. Similar to the Overflow module, it would allow us to extend an element and use its extension as a region, much like we did in the example in the previous section, where we used the pseudo-element as a region to flow the ads into. We could define as many pseudo-elements as we want to “extend” a region whenever needed, without having to add a single empty element to the DOM. Razvan Caliman has even created <a href="http://adobe.github.io/css-pseudo-elements/">a polyfill</a> for pseudo-elements if you’re interested you can check that out.</p>
<p>Another challenge we may face when creating regions is having text overflow a region when the text is scaled up (page zoomed in) by the user, or when the contents of a region change such as when the page is translated into another language, for example. A lot of times, we may want to keep the contents of a region inside that region, and <em>not</em> have them flow into another region, even if that other region is an extension of the main region.</p>
<p>For this, the CSS Regions specification extends break properties from the Multi-Column Layout specification. These properties can be used to specify whether content inside a region is allowed to overflow to another region or not. More specifically, they allow us to decide whether a region breaks or does not break relative to the content inside it. This technique is useful for keeping related elements grouped together and for preventing important elements from being split.</p>
<p>The following image is borrowed from <a href="https://twitter.com/cjgammon">CJ Gammon</a>'s <a href="http://coding.smashingmagazine.com/2013/11/05/killer-responsive-layouts-with-css-regions/">article on Smashing Magazine</a> and shows how the break properties can work when shuffling a layout to fit on small screens. They allow us to prevent the regions from breaking until after each image in the image flow. This is an excellent use case for CSS Regions. You can read more about the example in the article on Smashing Magazine.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/break-demo.gif" alt="Image showing how regions break on small screens" />
</figure>
<p>Brian Rinaldi has also touched on the topic of using CSS Regions in responsive layouts more then once. You can read more about creating simple and more complex layouts with CSS Regions in the following two articles he wrote:</p>
<ul>
<li><a href="http://flippinawesome.org/2014/01/27/using-css-regions-in-responsive-designs/">Using CSS Regions In Responsive Designs</a></li>
<li><a href="http://www.adobe.com/inspire/2014/01/complex-layouts-reflow-css-regions.html">Creating complex layouts for the web with CSS Regions and Adobe Edge Reflow CC</a></li>
</ul>
<h3 class="deeplink" id="use-cases-and-reusability">CSS Regions Use Cases And Reusability Of Code</h3>
<p>We can create reusable code using CSS Regions, similar to the way it is possible to create usable components using <a href="http://w3c.github.io/webcomponents/explainer/">Web Components</a>. The following examples show some use cases for CSS Regions that would otherwise not be possible without them. The code used to create the functionality in these examples is reusable.</p>
<p>I’m sure that once the new features like Grids and Flexbox are more widely supported, a lot more use cases for Regions may rise, so please do not take these examples as the <em>only</em> use cases for CSS Regions.</p>
<h4 class="deeplink" id="shuffling-layouts">Using Regions To Shuffle Layouts On Different Screen Sizes</h4>
<blockquote class="twitter-tweet" lang="en"><p>Regions give you the possibility to freely reshuffle the whole layout at any breakpoint. Even better than flex </p>— Christian Schaefer (@derSchepp) <a href="https://twitter.com/derSchepp/statuses/426282824002727936">January 23, 2014</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The example we saw earlier about “folding” content and displaying ads in the middle of articles on small screens is a perfect example of the ability of CSS Regions to shuffle layouts, which makes them a fantastic tool to have in our responsive web design toolkit.</p>
<p>The code used to create the ad-shuffling layout can be reused in any layout that needs the same functionality—you add the necessary regions where you want the ads to appear (flow into), and use the same couple of CSS lines shown earlier to flow the contents of the sidebar (or whichever container contains the ads) into these regions on smaller screens. And, of course, you could also avoid using those extra elements as regions and use the pseudo-element technique for adding the regions between the sections of the article.</p>
<p>In addition to defining regions where you want the ads to flow into, you can also style these regions’ content using the <code>@region</code> rule. A <code>@region</code> rule contains style declarations specific to a particular region. This also allows for even more modular style sheets that are specific to certain regions. This way, you can add your regions into a page, and hook in their specific style sheet, and end up with contents flowing into those regions being styled the way you expect them to, based on the styles u specify using the <code>@region</code> rule.</p>
<p>For example, the following snippet will style all paragraphs that flow into a region by applying an italic font style to them.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@region</span> #my-region</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-style</span><span class="token punctuation">:</span> italic<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>It is important to note here that, at the time of writing of this article, only a subset of styles are implemented for region styling. Only styles that don’t affect layout can now be applied to content inside regions using <code>@region</code>, like, for example, text and background colors. Styles that do affect layout like display, float, and margin properties do not yet work inside regions. But once they do, we can style region-specific content better and use it to create modular and reusable code. Also, the <strong>styles inside the region are applied to the source element</strong> which is going to be moved into the region, so you’ll need to remember to use the appropriate class names or IDs as the ones you’re using on your content.</p>
<h4 class="deeplink" id="repositioning-menus">Repositioning Menus On Small Screens</h4>
<p>In this example I am going to use just a couple of lines of CSS to move a navigation from the header into a hidden off-canvas sidebar on small screens. For the sake of brevity and clarity of code, I kept the sidebar visible because I wanted to focus only on the code that accomplishes the responsive menu effect using Regions, and not the actual hiding and showing of that menu.</p>
<p>The example is pretty basic, so I’ll just create a navigation menu in a header, and a sidebar to flow the menu into on small screens.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Logo<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>navigation<span class="token punctuation">'</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-nav<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Clients<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Contact Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-region<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span></code></pre>
<p>Only two lines of CSS are needed to move the navigation from the header into the sidebar. And only a few lines of CSS inside an <code>@region</code> rule are needed to apply styles to the contents inside our region. The navigation which is going to be moved into out region has new styles declared for it inside the <code>@region</code> rule. You can reuse this piece of code anywhere. Just drop that sidebar in and hook those few lines of CSS. Of course make sure the class names and IDs match those of the elements on the page you’re using them for.</p>
<p>Because, at this time, only a subset of styles are supported for region styling, I just changed the color of the links inside the sidebar for demonstration purposes, but when more styles are possible, we could have the navigation items stack on top of each other and be styled just like all off-canvas menus we see today.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* region-specific styles */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@-webkit-region</span> #my-region</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">.my-nav a</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token comment">/* flow the menu from the header into the sidebar on small screens */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span>32em<span class="token punctuation">)</span></span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">header nav</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-into</span><span class="token punctuation">:</span> navigation<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">#my-region</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-from</span><span class="token punctuation">:</span> navigation<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can see the live demo and check the code out <a href="http://codepen.io/SaraSoueidan/pen/0be636b17d2cbdef26ce2e57c3624478">here</a>. Resize the screen to see the effect.</p>
<p><a href="http://twitter.com/razvancaliman">Razvan Caliman</a> has also created a slightly more complex responsive menu that uses CSS Regions to flow menu items into a drop-down menu when these items no longer fit on the menu bar. You can see Razvan’s demo <a href="http://codepen.io/oslego/pen/tdHEg">here</a>.</p>
<h3 class="deeplink" id="regions-for-creating-columns">Examples Of Where One Might Want To Use CSS Regions To Create Columns</h3>
<p>There is no way in the CSS Multi-Columns module (that I know of) that gives designers the ability to style each column of text individually. The module allows content to be displayed in multiple columns, but it does not give us the power to treat each individual column separately, and give the columns different widths and heights, for example. And this is where CSS Regions have an advantage over Columns.</p>
<p>Columns created using CSS Regions are stylable any way we want them to. Each column is a region and each region can have its own styling independently of the other columns. And by styling I mean all kinds of styling: from adding backgrounds to a column, to styling text inside a column, to positioning columns, changing column dimensions, and even transforming columns using CSS Transforms.</p>
<p>Now, if creating a simple multi-column layout is the goal, and that layout can be achieved using Multi-Columns alone, then by all means we <em>should</em> use CSS Columns to do it. <strong>CSS Regions could do a lot of things, but they should not be used to do everything</strong>. Just because we can do something with Regions, does not mean that we should.</p>
<p>The first example shows three columns on the right page of a magazine design, each with different heights. Giving different heights to columns is not possible with CSS Multi-Columns alone. But using CSS Regions, each column can be defined as a region and given any height we want.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/styled-columns-1.png" alt="Picture showing a magazine layout with columns of different heights" />
</figure>
<p>And another example is one I recently created for <a href="http://sarasoueidan.com/blog/css-regions-with-shapes-for-readability">an article</a> I wrote about combining CSS Regions with CSS Shapes to improve the readability of layouts created using CSS Shapes.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/styled-columns-3.png" alt="Screenshot showing a magazine layout with columns styled using CSS Shapes" />
</figure>
<p>Each column shown in the above picture is a region, and each region is shaped using CSS Shapes to get the above result. You can view the live demo <a href="http://sarasoueidan.com/demos/fragmented-magazine-layout/">here</a>.</p>
<p>None of these examples would be possible to create using just CSS Multi-Columns, so it is great to have such a tool at our disposal.</p>
<p>And the final example I want to show is one created by Opera’s Chris Mills for an <a href="http://dev.opera.com/articles/view/an-introduction-to-css-regions/">introductory article</a> he wrote about CSS Regions. The demo is an example of how CSS Regions can be used to create text containers that are separate from each other, transformed using CSS Transforms, and positioned on the screen, and then have the text content flow into these containers in the order they appear in the original source.</p>
<blockquote>
In effect, we are completely separating out our content from any kind of layout, bringing the original intention of CSS to a welcome extreme. The content can be flowed into your layout containers regardless of how they are sized and positioned, and will reflow when those containers change size.—Chris Mills
</blockquote>
<p>This is a screenshot of the demo:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/opera-demo.png" alt="Screenshot of a demo using CSS Regions to flow content into containers transformed using CSS Transforms" />
</figure>
<p>The demo is a nice demonstration of how CSS Regions offer us finer control over fragments of a page and how they allow us to flow content inside containers any way we want them to, while giving us the ability to completely separate content from layout concerns.</p>
<h3 class="deeplink" id="regions-vs-future-layouts">CSS Regions Versus Future Layout Features</h3>
<p>CSS Regions can be used today to create and shuffle layouts in many ways. But what happens if a better solution is someday introduced that allows us to do what Regions allow us to do today in a better and more efficient way?</p>
<p>Mr. Håkon Wium Lie has already shown some good solutions to certain layout problems he discussed in his article by using <a href="http://figures.spec.whatwg.org/">CSS Figures</a> instead of CSS Regions. This is actually great, considering that the Figures and Multi-Columns specs are made to do what he used them to do. And when these specifications are implemented, we will probably use <em>them</em> to do what he did too, as they offer a more flexible way to create those layouts than Regions do.</p>
<p>As a matter of fact, <a href="http://figures.spec.whatwg.org/">the CSS Figures specification</a> introduces some really cool and useful features, like vertical floating, where we get to float an element using <code>top</code> and <code>bottom</code> values, in addition to the <code>left</code> and <code>right</code> values that we have today. I can imagine how these values can be useful already.</p>
<p>We also get a <a href="http://figures.spec.whatwg.org/#the-float-offset-property">float offset property</a> which allows us to offset a floated element, which is super useful considering how easy it makes to wrap content around a figure such as an image or a blockquote. We are now able to offset any element by applying a <code>position:relative</code> to it and then using the offset properties (<code>left</code>, <code>right</code>, <code>top</code>, <code>bottom</code>) to “push” it in any direction we want, but that will not affect the flow of content around it, as the position it occupies on the page will be preserved and won’t be occupied by any surrounding element. Using float offsets introduced in the CSS Figures spec, we will be able to wrap content around an offsetted float with so much ease, and this is just brilliant!</p>
<p>When new features such as CSS Figures are implemented, we will definitely use those for a lot of use cases where they would fit in better than CSS Regions, but CSS Regions also have other use cases that neither the Multi-Column specification, nor the Figures specification can do. So I believe that it would be great for the future of the web for all of these tools and features to coexist and offer us, designers and developers, a wider range of options that give us more control over our layouts.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>CSS Regions give us the ability to do a lot of things that are otherwise not possible without them. They can be a very useful and powerful tool in our responsive web design toolset, and when used in conjunction with other features, can provide us with great solutions to common design problems we are faced with.</p>
<p>Just like any other feature, CSS Regions should be used in the right place, and it is our responsibility to use them that way. I believe that we are smart enough to know when and where to use a feature.</p>
<p>Do you have any great use cases for CSS Regions? Do you think it will be a valuable tool in our web design toolset?</p>
<p class="note">
I published this article originally on <a href="http://flippinawesome.org/2014/01/27/css-regions-matter/">flippinawesome.org</a> on January 27<sup>th</sup>, 2014.
</p>
Animating CSS Shapes with CSS Animations and Transitions
2014-01-14T00:00:00Z
https://sarasoueidan.com/blog/animating-css-shapes/
<p class="deck">
Today we're going to be talking about animating CSS shapes with CSS animations.
We'll be creating very basic "shape-shifting" layouts of sort.
There are also many considerations to take when animating CSS shapes, so we'll go over all points
in this article.
</p>
<p>This is the third article in a series of articles I’m writing about CSS shapes,
so in this article I’m assuming you have a basic understanding of how CSS shapes are created.
You may want to check the first article: <a href="http://sarasoueidan.com/blog/css-shapes/">Creating Non-Rectangular Layouts with CSS Shapes</a>, which covers all the basics to get you started creating CSS shapes in no time. The second article was about <a href="http://sarasoueidan.com/blog/css-regions-with-shapes-for-readability/">combining CSS shapes with CSS regions to create more readable layouts</a>, tackling one face of the accessibility of non-rectangular shaped layouts.</p>
<div class="note warning">
Notes:
<ul>
<li>Most of this article's demos use the `shape-inside` property, which has been temporarily <a href="https://bugs.webkit.org/show_bug.cgi?id=130698">removed from Webkit</a> and <a href="https://codereview.chromium.org/209443007/">Blink</a>.</li>
<li>The principles of animating shapes is applicable to both CSS Shapes *and* CSS Clipping masks. All the
demos in this article use clipping masks to visualize CSS Shapes. Hence, the shapes are animated as clipping paths as well. So, you will be able to see the shapes animate in webkit-based browsers at this time, but the content inside the shapes won't be affected by the shapes because of the current state of support for CSS Shapes.</li>
<li>For a detailed suppprt table for CSS Clipping and Masking, see the <a href="http://caniuse.com/css-masks">CanIUse.com Support table</a>.</li>
<li>
Also, check the <a href="http://caniuse.com/#feat=css-shapes">current state of browser support for CSS Shapes</a> out.
</li>
</ul>
</div>
<h3 class="deeplink" id="animatable-shapes">Animatable CSS Shapes</h3>
<p>There are two ways we can create a shape with CSS shapes: using an image URI which the browser uses to extract the shape from, and using one of the available CSS shapes functions, such as <code>polygon()</code> and <code>circle()</code>, among others.</p>
<p><strong>Shapes defined using an image are not animatable</strong>. So, if you define a shape using an image, specify a transition, and then define another shape using another image when the element is hovered, for example, the shape applied to the shape will change but it will not animate or transition into the new shape; it will just “jump” from one shape into another in an abrupt manner.</p>
<p><strong>The only way a shape can be animated is when it is defined using a <u>shape function</u>.</strong> Even so, there is one condition: <strong>the number of points defining the final shape
must be the same as the number of points defining the initial shape</strong>. When you think about it, makes perfect sense: you have a set of points that make up a shape, and then you move those points around and have them form another shape.</p>
<p>So the <strong>transitions on CSS shapes are really transitions on the individual points making up a shape</strong>. More specifically, <strong>transitions on the coordinates of each point</strong>, where the coordinates are interpolated as real numbers to allow animations and transitions, just like any other animatable CSS values.</p>
<p>When I first learned about this condition, I thought it would not be possible to change a simple shape into a more complex one that has more points defining it, but that’s actually not true, it is very doable: start by definining the points needed for the more complex shape, and then rearrange
those points to form the simple shape.</p>
<p>For example, even if you have 20 points making up the final shape, and you want to start with a simple rectangle defined by 4 points, you can still place those 16 extra points on the rectangular shape by simply placing them on one of the rectangle’s sides, without them affecting the shape of the rectangle. We’ll get into the examples shortly.</p>
<p>Now, thinking about the complex shape first does not at all mean that the initial shape must be more complex than the final shape, it just means that you will think ahead and prepare all the requirements before you start coding. You can easily animate a simple shape into a more complex one if you have all the necessary points defined.</p>
<h3 class="deeplink" id="considerations">Things To Consider Before Animating Your Shaped Layouts</h3>
<h3 class="deeplink" id="content-fitting">Content Fitting Inside Different Shapes</h3>
<p>One thing to consider when animating CSS shapes in general is the fact that the content that fits into one shape may not perfectly fit into another. You may start out with a simple shape, and when you animate that shape into a new one, the content inside that shape may overflow the new shape, or may be too little for the new shape. So you may need to resize the element as you change its shape as well, so, again, the design process has to be thought through in detail. This also means that it wouldn’t be very simple to depend on animated CSS shapes for dynamic content, unless you create the shapes dynamically with Javascript. But that’s a topic for another blog post ;)</p>
<h3 class="deeplink" id="readability">Animated Text Becomes Temporarily Unreadable</h3>
<p>Animating text composition in general, whether by animating Shapes or even a simple animation of width, will make the text temporarily unreadable. <em>(Thanks <a href="https://twitter.com/alanstearns">Alan</a> for bringing that to my attention)</em></p>
<p>While it might be an interesting graphical effect for smooth layout changes, the process of text changing its layout blocks the ability of the reader to read your content, and it is only after the animation is over that they will again be able to read the text. That is, of course, assuming that the Shapes are readable to begin with, as we noted before.</p>
<h3 class="deeplink" id="accessibility">General Accessibility</h3>
<p>Be careful what shapes you choose, and consider how they affect the readability of your content. And think twice before integrating CSS animations with CSS Shapes.</p>
<p>Also, shapes that are fine on big screens may not be fine on small screens. Personally, I would
stick with rectangular layouts on small screens, and progress to more complex (but still readable)
shaped layouts on big screens. But then again, this depends on the shapes you choose, and some
simple shapes are actually OK on small screens as well.</p>
<p>The aim of this article is to discuss and <strong>experiment</strong> with the different ways and options we have to animate CSS Shapes. <strong>Shapes animations should only be used in real projects when there is a practical use-case for these animations</strong> (and of course when there is more acceptable browser support ^^). I’m sure someone will think of a nice use-case. So, for now, let’s dig into the actual animation process and have some CSS Shapes fun!</p>
<h3 class="deeplink" id="example-1">1. Initial and Final Shapes Have Same Number Of Points</h3>
<p>We’ll start off with very simple examples and then progess to more complex ones. The examples in this section won’t deal with the number of points defining a shape. We’ll leave that for the second section.</p>
<p>The best way to visualize a shape animation it to use the shape properties in conjunction with the <code>clip-path</code> property, which will clip any excess parts of the element that are outside the defined shape. I’ve talked about using CSS clip paths with CSS Shapes in detail in my <a href="http://sarasoueidan.com/blog/css-shapes/"> first article</a>, so check that out if you still don’t know how they can relate.</p>
<h3 class="deeplink" id="rearranging-points">Rearranging Points To Create A New Shape</h3>
<p>So for our first example, we’re going to do just that. And for the sake of simplicity and demonstration purposes, we’re not going to be doing anything fancy. We’ll create a simple shape and animate it on hover using a simple CSS transition. The initial and final shapes will have the same number of points so we won’t have to worry about that for now. All we have to do, is move the points around (rearrange them) and see how the shape transitions smoothly.</p>
<p>We’ll start with a simple inverted trapezoid shape, and when you hover over the element, its shape will animate but the final shape will also be a trapezoid. The only thing we’re going here is move the points defining the shape around by changing their coordinate values.</p>
<p>We’ll be using the <code>clip-path</code> property to make the shape and the animation clearer and more obvious.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 500px 0<span class="token punctuation">,</span> 350px 300px<span class="token punctuation">,</span> 150px 300px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-padding</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> all 2s ease<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 500px 0<span class="token punctuation">,</span> 350px 300px<span class="token punctuation">,</span> 150px 300px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span> </span><br /><span class="highlight-line"><span class="token selector">.element:hover</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>150px 0<span class="token punctuation">,</span> 350px 0<span class="token punctuation">,</span> 500px 300px<span class="token punctuation">,</span> 0px 300px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>150px 0<span class="token punctuation">,</span> 350px 0<span class="token punctuation">,</span> 500px 300px<span class="token punctuation">,</span> 0px 300px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span> </span></code></pre>
<p>The shape-* properties are supported unprefixed in Canary, but the <code>clip-path</code> property still needs its prefix to work.</p>
<p data-height="500" data-theme-id="3617" data-slug-hash="6aa071b2202052b5fed38b1312f3878d" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/6aa071b2202052b5fed38b1312f3878d">animating a css shape</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>And it’s as simple as that! Just change the coordinates of the points defining the shape define a transition on the element, and you’ve got yourself a smooth animating shape.</p>
<p>You will be able to see how the text inside the shape is animated into the new shape as well. And you can also see that, during the animation process, that text is very unreadable.</p>
<p>Now, if you change the shape on hover to another shape by using another shape function for example, or changing the number of points defining the shape, the shape <em>does</em> change, but it will not transition or animate; it will jump from the initial shape to the final one abruptly. For example, the following code:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.demo:hover</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* blink needs old syntax at this time */</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>50%<span class="token punctuation">,</span> 50%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>50%<span class="token punctuation">,</span> 50%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* new syntax for webkit */</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>50% at 50% 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>50% at 50% 50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span> </span></code></pre>
<p>I’ve added this snippet to the above demo. Click the “Edit on Codepen” link on the demo, and uncomment the snippet in the <code>.element:hover</code> rule to see how it affects the animation.</p>
<h3 class="deeplink" id="resizing-shapes-with-no-points">Resizing Shapes With No Specific Number Of Points</h3>
<p>In our second example, we’re going to animate a circular shape by increasing its diameter. The number of points on a circle is undefined, and the only thing defining the circle in the <code>circle()</code> function is its center and radius.</p>
<p>We’re going to have the circle increase in size when we hover over it. This sounds a lot like resizing an element in CSS using CSS transforms and the <code>scale()</code> function, but it’s not really the same. When you scale an element using CSS transforms, the entire element including its content will increase in size, and by increasing its size you’re not creating any extra room inside it for more content. But when you resize an element’s defined Shape, you <em>are</em> making more room or taking away from it. So, you could, for example, increase the size of the element’s shape and then dynamically add some content to it and have that content fit inside it, but that’s not possible when you’re scaling the element up with CSS transforms.</p>
<p>In this example we’ll first define a circular shape on our element using the <code>circle()</code> shape function, and then animate the circle’s size when the element is hovered. You will be able to see how that will affect the room inside the element.</p>
<p>First, we’re going to define the shape on the element and fire an animation on hover. We’ll define a circle of radius <code>210px</code> whose center is positioned at the center of the element.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"> <span class="token property">shape-padding</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* blink */</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* webkit */</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>210px at 250px 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>210px at 250px 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span> </span><br /><span class="highlight-line"><span class="token selector">.element:hover</span><span class="token punctuation">{</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-animation</span><span class="token punctuation">:</span> scale 3s ease infinite<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span> </span></code></pre>
<p>Then we’re going to define the animation keyframes that will control the scaling of the shape. The final effect will be similar to a beating effect: the radius of the circle is going to increase and then decrease back to its initial size at the end of the animation. And we have set the animation to repeat infinitely as long as you’re hovering over the element.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@keyframes</span> scale</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">0%</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>210px at 250px 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">50%</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px at 250px 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">0%</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>250px<span class="token punctuation">,</span> 250px<span class="token punctuation">,</span> 210px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>210px at 250px 250px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And here is the resulting effect:</p>
<p data-height="600" data-theme-id="3617" data-slug-hash="acb88a202823fd107d1327f4b1a9c3c2" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/acb88a202823fd107d1327f4b1a9c3c2">animating a css shape</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>You can see that the room for content inside the circle increases, and more text can fit inside the shape when it grows. So, you could, for example, use a growing CSS Shape to show more content information when an element is hovered.</p>
<h3 class="deeplink" id="example-2">2. Initial and Final Shape Have Different Number Of Points</h3>
<p>In this section we’re to animate from one shape to another, where the initial and final shapes are <em>visually</em> defined by a different number of points. We’re going to use the <code>polygon()</code> function to define our shapes.</p>
<p>As I mentioned at the beginning of this article, only shapes with the same number of points can be animated into one another. So, in order to animate from a shape to another, we have to make sure that the number of points in the definition of these shapes is the same, even if they don’t visually appear to have the same number.</p>
<h3 class="deeplink" id="animating-complex-to-simple">Animating A Complex Shape Into a Simple Shape</h3>
<p>We’re going to start with a very simple demo, where we animate a star shape into a simple rectangle shape. (Not that the star shape is really complex, but it is more complex than the final shape it will be animating into.)</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/star-to-rect.jpg" alt="The two shapes" />
<figcaption>
The two shapes we'll be animating. Blue discs show the number of points needed to define the shape.
</figcaption>
</figure>
<p>The star is defined by 10 points (shown in blue), and the rectangle only needs 4 points to define it. It is true that the rectangle only <strong>needs</strong> four points to define it, but it can also be defined by as many points as we want it to.</p>
<p>The <code>polygon()</code> function we’re going to use to define these two shapes <strong>needs to take in the same number of points for the two shapes</strong> in order for the animation to be possible. This means that we need to use 10 points to draw the rectangle just like we will use 10 points to draw the star, and there is no problem with this. Why? Because we can simply place the extra points in a way that does not affect the resulting shape we want.</p>
<p>So, the rectangle will be defined by 10 points, which we can place as shown in the image below:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/rect-points.jpg" alt="points placed on the rectangle" />
<figcaption>
The rectangle can be defined by as many points as the star. This is needed to make the animation work.
</figcaption>
</figure>
<p>Of course, we could have placed the extra 6 points anywhere on the rectangle’s edges, but that would change the animating effect. We’ll get to this in a minute.</p>
<p>Now that we have the same number of points in both shapes, we can easily transition from one shape to another.</p>
<p>We’ll first position the points so that they make up a star. The <code>polygon()</code> function for the star shape looks like following; of course, we’re also going to clip the element to the shape using the <code>clip-path</code> property as we did before.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>250px 0<span class="token punctuation">,</span> 350px 170px<span class="token punctuation">,</span> 500px 180px<span class="token punctuation">,</span> 380px 320px<span class="token punctuation">,</span> 450px 500px<span class="token punctuation">,</span> 250px 420px<span class="token punctuation">,</span> 50px 500px<span class="token punctuation">,</span> 120px 320px<span class="token punctuation">,</span> 0px 180px<span class="token punctuation">,</span> 150px 170px <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-padding</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> all 3s ease<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>250px 0<span class="token punctuation">,</span> 350px 170px<span class="token punctuation">,</span> 500px 180px<span class="token punctuation">,</span> 380px 320px<span class="token punctuation">,</span> 450px 500px<span class="token punctuation">,</span> 250px 420px<span class="token punctuation">,</span> 50px 500px<span class="token punctuation">,</span> 120px 320px<span class="token punctuation">,</span> 0px 180px<span class="token punctuation">,</span> 150px 170px <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And when the element is hovered, the points will be rearranged to form a rectangle defined as follows:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.element:hover</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"><span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>250px 0<span class="token punctuation">,</span> 500px 0<span class="token punctuation">,</span> 500px 180px<span class="token punctuation">,</span> 500px 320px<span class="token punctuation">,</span> 500px 500px<span class="token punctuation">,</span> 250px 500px<span class="token punctuation">,</span> 0 500px<span class="token punctuation">,</span> 0 320px<span class="token punctuation">,</span> 0 180px<span class="token punctuation">,</span> 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>250px 0<span class="token punctuation">,</span> 500px 0<span class="token punctuation">,</span> 500px 180px<span class="token punctuation">,</span> 500px 320px<span class="token punctuation">,</span> 500px 500px<span class="token punctuation">,</span> 250px 500px<span class="token punctuation">,</span> 0 500px<span class="token punctuation">,</span> 0 320px<span class="token punctuation">,</span> 0 180px<span class="token punctuation">,</span> 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can see the points being rearranged to form a rectangle in this live demo:</p>
<p data-height="700" data-theme-id="3617" data-slug-hash="17dd591f451f4757366faf3c9246504b" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/17dd591f451f4757366faf3c9246504b">animating a css shape</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>Rearranging the points has a very big effect on the transition. The order of points in the initial shape is preserved when they are rearranged to form the second one, and you need to keep that in mind, otherwise you may end up with a not-so-beautiful transition/animation effect.</p>
<p>The following is an example of what could happen if you randomly rearrange the points. You can see where each point will be placed, and you can tell that that is not the best way to do it.</p>
<p data-height="700" data-theme-id="3617" data-slug-hash="b49058b2d80de2c2463a42daf4d1c9aa" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/b49058b2d80de2c2463a42daf4d1c9aa">animating a css shape</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>So be careful and always make sure you rearrange the points the best possible way that ensures a nice transitioning effect.</p>
<h3 class="deeplink" id="animating-simple-to-complex">Animating A Simple Shape Into a Complex Shape</h3>
<p>Our last example is the most complex one, but you’ll find that it’s not really complex at all.</p>
<p>We’ll be applying the same concept we applied in the previous example, but instead of starting out with a higher number of points we’re going to start with a simple rectangular shape and have it animate into an irregular shape.</p>
<p>We’ll have a look at what the final shape looks like, how many points it needs to define it, and then well define the initial rectangle shape using the same number of points. The points will be placed on the edges of the rectangle, and we’ll make sure we place them in a suitable way so that they animate to the final shape the way we’d expect them to.</p>
<p>We want to start with a simple two-column layout. Each column is a separate rectangular element. And then when we hover over the columns’ container, a shape, a tree in our example, will show in the middle between the two columns, and the two columns will animate their shapes so that they kind of wrap the tree in the middle. This example is inspired by <a href="http://codepen.io/adobe/pen/Cnvuf">this pen</a> by the Adobe Web Platform Team.</p>
<p>So this is how the shapes in our our final demo will look like:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/demo-shapes.png" alt="The shapes of the columns in the final demo" />
<figcaption>
The image shows the shapes that the two text columns will animate to when their container is hovered. The shape on the left shows the points needed to define it. The shape on the right will have a similar point structure.
</figcaption>
</figure>
<p>I drew this in image Photoshop, even the example tree is actually a shape defined in Photoshop. Again, we’re not going to be doing anything fancy in this article, we’ll leave that to another one!</p>
<p>The shapes in the image are <em>similar</em> to the shapes in the final demo. Of course, there are no coordinates in the image above to the shapes will probably differ a bit when we plot their points on the elements’ coordinate systems. So, let’s get to it!</p>
<p>We’ll start by creating two columns of text inside a container. We’ll use the tree shape as a background to the entire container. At first, the background will be invisible, and then when the container is hovered, the background image will scale up, making it appear as if the tree is scaling up in the middle between the two columns. And as the tree appears, the two columns will animate into their respective shapes shown in the above image.</p>
<p>In order to know how many points are exactly needed, so that we can define them on the rectangle, I’m going to start by defining the <strong>final</strong> shape of the columns, and then move backwards and use the same number of points to define the rectangle.</p>
<p>The right column’s final shape can be defined by the following shape function:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 30px 550px<span class="token punctuation">,</span> 30px 450px<span class="token punctuation">,</span> 80px 400px<span class="token punctuation">,</span> 100px 400px<span class="token punctuation">,</span> 120px 400px<span class="token punctuation">,</span> 160px 350px<span class="token punctuation">,</span> 120px 250px<span class="token punctuation">,</span> 100px 200px<span class="token punctuation">,</span> 100px 170px<span class="token punctuation">,</span> 100px 160px<span class="token punctuation">,</span> 60px 130px<span class="token punctuation">,</span> 60px 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 30px 550px<span class="token punctuation">,</span> 30px 450px<span class="token punctuation">,</span> 80px 400px<span class="token punctuation">,</span> 100px 400px<span class="token punctuation">,</span> 120px 400px<span class="token punctuation">,</span> 160px 350px<span class="token punctuation">,</span> 120px 250px<span class="token punctuation">,</span> 100px 200px<span class="token punctuation">,</span> 100px 170px<span class="token punctuation">,</span> 100px 160px<span class="token punctuation">,</span> 60px 130px<span class="token punctuation">,</span> 60px 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span></code></pre>
<p>From the above shape, we can now define the initial rectangular shape, by using the same number of points but placing them on the rectangle’s edges. Because our shape changes only on the left side, we can place all the animating points on the left edge of the recangle, and then have them move horizontally into their places on hover. This means that it’s enough to use the same <code>polygon()</code> function as above, but move those points on the left of the shape to the left edge of the rectangle, by giving them all zero abscissa.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px<span class="token punctuation">,</span> 0 450px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 350px<span class="token punctuation">,</span> 0 250px<span class="token punctuation">,</span> 0 200px<span class="token punctuation">,</span> 0 170px<span class="token punctuation">,</span> 0 160px<span class="token punctuation">,</span> 0 130px<span class="token punctuation">,</span> 0 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px<span class="token punctuation">,</span> 0 450px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 350px<span class="token punctuation">,</span> 0 250px<span class="token punctuation">,</span> 0 200px<span class="token punctuation">,</span> 0 170px<span class="token punctuation">,</span> 0 160px<span class="token punctuation">,</span> 0 130px<span class="token punctuation">,</span> 0 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span></code></pre>
<p>So the final code to animate the shape of the right column when its container is hovered looks like the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.column-right</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px<span class="token punctuation">,</span> 0 450px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 350px<span class="token punctuation">,</span> 0 250px<span class="token punctuation">,</span> 0 200px<span class="token punctuation">,</span> 0 170px<span class="token punctuation">,</span> 0 160px<span class="token punctuation">,</span> 0 130px<span class="token punctuation">,</span> 0 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px<span class="token punctuation">,</span> 0 450px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 400px<span class="token punctuation">,</span> 0 350px<span class="token punctuation">,</span> 0 250px<span class="token punctuation">,</span> 0 200px<span class="token punctuation">,</span> 0 170px<span class="token punctuation">,</span> 0 160px<span class="token punctuation">,</span> 0 130px<span class="token punctuation">,</span> 0 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.container:hover .column-right</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 30px 550px<span class="token punctuation">,</span> 30px 450px<span class="token punctuation">,</span> 80px 400px<span class="token punctuation">,</span> 100px 400px<span class="token punctuation">,</span> 120px 400px<span class="token punctuation">,</span> 160px 350px<span class="token punctuation">,</span> 120px 250px<span class="token punctuation">,</span> 100px 200px<span class="token punctuation">,</span> 100px 170px<span class="token punctuation">,</span> 100px 160px<span class="token punctuation">,</span> 60px 130px<span class="token punctuation">,</span> 60px 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 30px 550px<span class="token punctuation">,</span> 30px 450px<span class="token punctuation">,</span> 80px 400px<span class="token punctuation">,</span> 100px 400px<span class="token punctuation">,</span> 120px 400px<span class="token punctuation">,</span> 160px 350px<span class="token punctuation">,</span> 120px 250px<span class="token punctuation">,</span> 100px 200px<span class="token punctuation">,</span> 100px 170px<span class="token punctuation">,</span> 100px 160px<span class="token punctuation">,</span> 60px 130px<span class="token punctuation">,</span> 60px 110px<span class="token punctuation">,</span> 0 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Similarly, we can get the shape functions for the left column. First define the final (more complex) shape, to get the necssary number of points. Then rearragne those points into a rectangle.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.column-left</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 60px<span class="token punctuation">,</span> 300px 80px<span class="token punctuation">,</span> 300px 100px<span class="token punctuation">,</span> 300px 150px<span class="token punctuation">,</span> 300px 180px<span class="token punctuation">,</span> 300px 275px<span class="token punctuation">,</span> 300px 375px<span class="token punctuation">,</span> 300px 420px<span class="token punctuation">,</span> 300px 410px<span class="token punctuation">,</span> 300px 440px<span class="token punctuation">,</span> 300px 450px<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 60px<span class="token punctuation">,</span> 300px 80px<span class="token punctuation">,</span> 300px 100px<span class="token punctuation">,</span> 300px 150px<span class="token punctuation">,</span> 300px 180px<span class="token punctuation">,</span> 300px 275px<span class="token punctuation">,</span> 300px 375px<span class="token punctuation">,</span> 300px 420px<span class="token punctuation">,</span> 300px 410px<span class="token punctuation">,</span> 300px 440px<span class="token punctuation">,</span> 300px 450px<span class="token punctuation">,</span> 300px 550px<span class="token punctuation">,</span> 0 550px <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.container:hover .column-left</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 60px<span class="token punctuation">,</span> 280px 80px<span class="token punctuation">,</span> 240px 100px<span class="token punctuation">,</span> 230px 150px<span class="token punctuation">,</span> 200px 180px<span class="token punctuation">,</span> 160px 275px<span class="token punctuation">,</span> 130px 375px<span class="token punctuation">,</span> 160px 420px<span class="token punctuation">,</span> 240px 410px<span class="token punctuation">,</span> 270px 440px<span class="token punctuation">,</span> 290px 450px<span class="token punctuation">,</span> 290px 550px<span class="token punctuation">,</span> 0 550px <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 300px 0<span class="token punctuation">,</span> 300px 60px<span class="token punctuation">,</span> 280px 80px<span class="token punctuation">,</span> 240px 100px<span class="token punctuation">,</span> 230px 150px<span class="token punctuation">,</span> 200px 180px<span class="token punctuation">,</span> 160px 275px<span class="token punctuation">,</span> 130px 375px<span class="token punctuation">,</span> 160px 420px<span class="token punctuation">,</span> 240px 410px<span class="token punctuation">,</span> 270px 440px<span class="token punctuation">,</span> 290px 450px<span class="token punctuation">,</span> 290px 550px<span class="token punctuation">,</span> 0 550px <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And of course in order to get a smooth shape transition, we need to define a transition on the two columns, and a transition on the container with the same speed and timing function, so that the background and column shapes can animate simultaneously.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 700px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 30px auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 25px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> #eee <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>http://sarasoueidan.com/blog/animating-css-shapes/images/tree.png<span class="token punctuation">)</span></span> 50% 50% no-repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> all 1s linear<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token comment">/* scale the background up on hover */</span></span><br /><span class="highlight-line"><span class="token selector">.container:hover</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 50% auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.column</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* height and width must be explicitly set otherwise Shapes won't work */</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 550px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">text-align</span><span class="token punctuation">:</span> justify<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #ddd<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* define same transition duration and timing function as the container's */</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> all 1s linear<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">shape-padding</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And this is the final working demo:</p>
<p data-height="750" data-theme-id="3617" data-slug-hash="94e3c9210c418770206487ef8700a1c2" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/SaraSoueidan/pen/94e3c9210c418770206487ef8700a1c2">94e3c9210c418770206487ef8700a1c2</a> by Sara Soueidan (<a href="http://codepen.io/SaraSoueidan">@SaraSoueidan</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>It is worth noting here that if the two columns were completely filled with text before they are animated, that text will overflow the shape that it will animate to. This is one of those cases where you would want to take into account the text and shapes before you decide to animate your element’s shape.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>In this article we went over the basics of animating CSS shapes. All of the shapes we animated here were static, i.e defined in the CSS and animated there. But sometimes, in order to achieve more compelling visual effects, you may want to dynamically create shapes while some element moves on the page. That kind of shape animations can be achieved by creating and animating CSS shapes using Javascript, and is, for now, outside the scope of this article.</p>
<p>The examples and demos I showed in this article are all for demonstration purposes only, and may not make for a practical use-case for animated CSS shapes. But combined with CSS clipping paths, some creative shape-shifting layouts can be created that don’t compromise readability of the content.</p>
<p>I hope this article, along with the previous two, helped you get up and running with CSS shapes. Of course, at this time, support for CSS Shapes is still limited, but I highly encourage you to start experimenting with them now, as you could help find and fix bugs, and of course when the time comes and Shapes are widely supported, you’ll be Shapes masters by then. :)</p>
<p>I hope you found this article useful. Thank you very much for reading!</p>
Using CSS Regions With CSS Shapes For A Better Reading Experience
2013-12-05T00:00:00Z
https://sarasoueidan.com/blog/css-regions-with-shapes-for-readability/
<p class="deck">
Using <a href="http://dev.w3.org/csswg/css-shapes/">CSS shapes</a> we can flow our content in non-rectangular areas. And sometimes we want to be able to flow our content into multiple custom-shaped areas inside an element. If you've read my <a href="http://sarasoueidan.com/blog/css-shapes/index.html">previous article</a>, you already know that this can be done with CSS Shapes, by using an image with alpha transparency with multiple shapes defined in it, and letting the browser extract the content's float areas from it. As appealing as this may sound and as creative as we can get with our shapes, flowing the text into multiple areas can easily make our content almost completely unreadable.
</p>
<small>
This article assumes that you're already familiar with the basics of CSS Shapes and <a href="http://dev.w3.org/csswg/css-regions/">CSS Regions</a>. I've recently written <a href="http://sarasoueidan.com/blog/css-shapes/index.html">an in-depth comprehensive article about creating non-rectangular layouts with CSS shapes</a>, which is perfect for getting you up and running with CSS shapes.
</small>
<div class="note warning">
Notes:
<ul>
<li>
This article's demo uses the
<code>shape-inside</code> property, which will be temporarily <a href="https://bugs.webkit.org/show_bug.cgi?id=130698">removed from Webkit</a> and <a href="https://codereview.chromium.org/209443007/">Blink</a>. So, for the time being, this article will only show screenshots of how the demo works when <code>shape-inside</code> is implemented again.
</li>
<li>
CSS Regions have also been <a href="http://www.cnet.com/news/reversing-course-google-rejects-adobe-web-publishing-tech/">dropped from Blink</a>, so they currently don't work in Chrome.
</li>
<li>
Check the <a href="http://caniuse.com/#feat=css-shapes">current state of browser support for CSS Shapes</a> out.
</li>
</ul>
</div>
<p>CSS shapes can introduce a serious accessibility problem when not used wisely. It’s great to achieve all kinds of creative layouts with custom shapes, but the first and most important thing to keep in mind is that the content is there to be read, so a designer must think accessibility and readability first, then appealing layout second.</p>
<p>To explain this better, let’s get into an example of when CSS shapes can cause a really bad reading experience. Although, to be fair, it’s not CSS shapes that does that, it’s the decision the designer makes, but you know what I mean.</p>
<h3 class="deeplink" id="the-problem">The Problem: Multiple CSS Shapes Making Text Unreadable</h3>
<p>A few days ago I <a href="https://twitter.com/SaraSoueidan/status/407909116862943232">tweeted</a> about a <a href="http://www.behance.net/gallery/Magazine-Feature-Magazine/9812813">“fragmented” magazine layout</a> I made using CSS Shapes and <a href="http://www.w3.org/TR/css-masking/">CSS Masks</a>. The layout took literally less than 2 minutes to create after the mask images were ready (I made those using Photoshop). But after finishing the layout, I realized that, even though it looked quite interesting and creative, it was anything but readable.</p>
<p>Here’s how the layout looked:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/old-demo.png" alt="Fragmented Magazine Layout Demo Screenshot" />
<figcaption>Fragmented Magazine Layout created with CSS Shapes and Masks</figcaption>
</figure>
<p>The left part of the layout, where the text is flowing in 3 custom-shaped areas, is where the problem occurred.</p>
<p>As with content masking, you can imagine your content as one layer and the mask as another; the content will be “painted” only where the mask is opaque, assuming that we’re working with an alpha mask which has only fully black and fully transparent areas. In this case, the fully black areas determine the areas where the content will be visible. And in terms of CSS shapes, the black areas determine the flow areas where our text will flow.</p>
<p>This is the mask I used to create the 3 flow areas for the text:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragments-text-mask.png" alt="Fragmented Magazine Layout Mask Defining Flow Areas" />
<figcaption>The alpha channel mask used to define the flow areas for the text container</figcaption>
</figure>
<p>Now the problem occurs from the way the browser’s layout engine works to fill these areas with text. When the browser flows the text into the shapes it moves down an element line by line, and starts writing the text in the text’s flow areas, which, when you’re using a mask image, are the areas of the element covered by the black areas of the mask. For regular rectangular elements, i.e. those that don’t have any CSS shape defined on them, that is perfectly fine: just move down the element one line after another, and type the text on those lines. But when an element’s flow area changes, things can easily get messy.</p>
<p>The browser starts with the first line on an element, and prints the text on that line <em><strong>only where the line passes through a defined flow area (one of the black areas)</strong></em>. So, what happens is that words will end up “scattered” on every line, and the lines divided into several “fragments” depending on the number of flow areas passing through the line. A reader’s eye will have to “jump” from area to area to read the fragments that make up a line.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragments-text-mask-shapes-flow.png" alt="Content flow into defined flow areas with CSS Shapes" />
<figcaption>The horizontal "fragmented" flow of text inside multiple flow areas defined by CSS shapes</figcaption>
</figure>
<p>As you can see in the image above, if you start reading a line, <strong>your eyes have to jump from area to area for every line</strong>, which, after a few of lines, becomes impossible to do, at least for me.</p>
<p>When I first started experimenting with CSS shapes I expected the text to flow in each of these custom-shaped areas the same way it does in CSS regions: I expected the browser to treat CSS Shapes’ flow areas the same way it treats CSS regions, but it does not. What we would normally want to do, is have the text flow in the individual areas, moving to one area after another one has been filled with content.</p>
<p>One way to achieve this (filling areas one after another) is by using CSS regions to create any number of flow areas we want, and then giving each region a custom shape inside it using the <code>shape-inside</code> property. So, for the above mask for example, which has 3 shapes inside it which define 3 flow areas, we will define each of these areas as a region, and then give each region a custom shape so that text flows inside it the way we want it to. This way, the browser will fill the first area (first region) with text, and then when it’s full, it will move into the second region after the first one’s been filled, and so on. This way, the text can be easily read inside each region (as long as you’re not using some crazy shape that will make text unreadable in all cases), as your eyes won’t have to jump between regions for every line.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragments-text-mask-regions-flow.png" alt="Content flow into flow areas defined with CSS Regions and shapes" />
<figcaption>Using CSS regions to define the three areas, text will fill each region before flowing into the next one, making reading a lot easier</figcaption>
</figure>
<p>Now let’s change the above demo I made by introducing CSS regions into it and see how that affects the readability of the layout.</p>
<h3 class="deeplink" id="the-solution">The Solution: Introducing CSS Regions Into The Layout</h3>
<p>What we’re going to do, is create three regions inside our text container, and then give each region a shape using <code>shape-inside</code> so that the text flows inside it in a non-rectangular manner.</p>
<p>The right side of the demo, which is a “fragmented” image, is created using CSS masks. A mask is applied to the element, which will “erase” parts of the image where the mask is transparent. We’ll get to this part at the end of this section.</p>
<p>First, we’re going to go over the markup for the two “pages”. The page with a class name <code>.text</code> is the left page with the text inside it. Inside this page, we’re going to define 3 regions, and a <code>.content</code> container which will contain the text that we want to flow into these regions.</p>
<p>So, the 3 regions are initially empty, and via CSS, we’re going to fill them with the text contained in the <code>.content</code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>magazine<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Good design should be used solely as information that acknowledges its very difficult to leave room for the consumer with promises that part of design is only lousy design emphasises the intellectual side .... </span><br /><span class="highlight-line"> <span class="token comment"><!--...--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>region region-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>region region-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>region region-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page photo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>images/bg.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Photo of a Sea<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now that we have our markup ready, we’ll start by defining some general styles before we get into the relevant CSS. We’ll be giving all the elements fixed dimensions; both CSS shapes and CSS regions require an element to have fixed dimensions to work.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.magazine</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 700px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1300px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> .8em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 1em auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Nunito"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.page</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 710px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 700px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> .9em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.photo</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>590px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.photo img</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* .. */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.text</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* We're justifying the text so that it fills the entire line so that the shapes are more "defined" by their content */</span></span><br /><span class="highlight-line"> <span class="token property">text-align</span><span class="token punctuation">:</span> justify<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.region</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* must specify dimensions on regions otherwise they won't be filled with text */</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 220px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 670px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> .5em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>With these styles set, we can now flow our text content into our regions using CSS’s <code>flow-from</code> and <code>flow-into</code> properties. I’ll be using the <code>-webkit-</code> prefix as the demo will only work in webkit browsers at this time.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.content</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-into</span><span class="token punctuation">:</span> pocket<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.region</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-flow-from</span><span class="token punctuation">:</span> pocket<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>That’s all you need to flow the text into the regions. The demo would look like the following screenshot. I’ve colored the regions’ background so that they are more visible. And for the time being, you can ignore the right side of the page with the fragmented image effect. Like I mentioned earlier, we’ll get to that by the end of this section.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragmented-magazine-regions-show.jpg" alt="Screenshot of Demo with Regions and Mask applied" />
<figcaption>Screenshot of the demo when text flows inside the regions before applying the shapes to them</figcaption>
</figure>
<p>The text flows from one region to another starting from the left column to the right, which is exactly what we set out to achieve. Now what we want to do next is apply the same non-rectangular shapes to our 3 columns (regions) as those we saw in the mask used above, so that the end result looks like the demo we want to improve.</p>
<p>Because we want to change the flow of text <em>inside</em> the regions, we’re going to use CSS’s <code>shape-inside property to change the shape of the flow area inside the regions. There are two ways we could define our shapes: using an image with an alpha channel like we did in the initial demo before we introduced regions, or by creating these shapes using the <code>polygon()</code> shape function.</code></p>
<p>Because each region will have only one flow area inside it which is a simple polygonal shape, we’re going to use the <code>polygon()</code> function instead of creating 3 images for the 3 regions (the image previously used in the demo to define the 3 flow areas can be divided into 3 masks using Photoshop, each mask containing one of the shapes inside the image).</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.region-1</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>80px 0px<span class="token punctuation">,</span> 150px 0<span class="token punctuation">,</span> 190px 200px<span class="token punctuation">,</span> 130px 400px<span class="token punctuation">,</span> 200px 550px<span class="token punctuation">,</span> 200px 670px<span class="token punctuation">,</span> 30px 670px<span class="token punctuation">,</span> 0px 100px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.region-2</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>150px 0px<span class="token punctuation">,</span> 210px 170px<span class="token punctuation">,</span> 150px 300px<span class="token punctuation">,</span> 220px 670px<span class="token punctuation">,</span> 50px 670px<span class="token punctuation">,</span> 5px 430px<span class="token punctuation">,</span> 40px 270px<span class="token punctuation">,</span> 0px 60px<span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.region-3</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>190px 0px<span class="token punctuation">,</span> 210px 350px<span class="token punctuation">,</span> 150px 610px<span class="token punctuation">,</span> 140px 670px<span class="token punctuation">,</span> 0px 370px<span class="token punctuation">,</span> 40px 160px<span class="token punctuation">,</span> 10px 40px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And now the text flows inside our regions in non-rectangular shapes:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragmented-magazine-regions-shaped.jpg" alt="Screenshot of Demo with Shapes applied to Regions" />
<figcaption>Screenshot of the demo when shapes are applied to the regions</figcaption>
</figure>
<p>With this result, it’s a lot easier to read the text than it was in the intial demo. Your eyes can move down a column and then move to the next once it’s done with the first.</p>
<p>Nonetheless, it’s absolutely necessary that a designer always design shapes that don’t strain the eyes. There’s probably a good reason why rectangular reading areas are the most comfortable to read in, and when designers decide to think outside the box (pun intended), it’s important to remember not to compromise readability and sacrifice a good user experience for beautiful comps.</p>
<p>With that said, this is how the demo looks:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/css-regions-with-shapes-for-readability-header.png" alt="Screenshot of the final demo" />
<figcaption>Screenshot of the final demo</figcaption>
</figure>
<p>And this is how CSS regions can help create a fairly better reading experience when used with CSS shapes. It would be great if browsers filled the CSS shapes the same way it filled CSS regions, but because it’s not a simple task to just change the layout algorithm, we can always use CSS regions to get that result.</p>
<h3 class="deeplink" id="regions-with-masks">Creating The "Fragmented" Photo Effect with CSS Masks</h3>
<p>Last but not least, we’re going to use CSS masks to mask parts of the image in the <code>.photo</code> page, to give it that “fragmented” effect. The mask used to create this effect on the image looks like so:</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/fragments-mask.png" alt="Mask used to create the fragmented effect on the image" />
<figcaption>Mask with alpha channel used to create the "fragmented" effect on the image</figcaption>
</figure>
<p>The above mask is an image with an alpha channel, where the opaque areas define where the content will be visible, and the transparent areas are the areas where the content will be “erased”. You can notice that it’s obviously a messy mask :P because that’s what happens when I try to “draw” with a mouse! But then again, it kind of emphasizes that “torn” and “fragmented” effect on the image, which is nice. =)</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.photo img</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* ... */</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/* mask applied to the image. At this time, only webkit browsers support CSS masks with a webkit prefix */</span></span><br /><span class="highlight-line"> <span class="token property">-webkit-mask-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>../images/fragments-mask.png<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>CSS Shapes will be one of the best things to happen to design on the web, but as we’ve always learned: with great power comes great repsonsibility. It’s <a href="http://blogs.adobe.com/webplatform/2013/12/04/css-shapes-in-last-call/">estimated</a> that in a year from now many browsers will have implemented CSS shapes, so it’s great to start experimenting with them from now. The more you experiment the more you can help find bugs to fix them before all browsers implement them.</p>
<p>And finally, I hope you enjoyed reading this article and found it useful. Thank you for reading.</p>
Techniques For Creating Textured Text
2013-12-02T00:00:00Z
https://sarasoueidan.com/blog/textured-text-techniques/
<p class="deck">
For too long, we've resorted to graphics editors to create <em>images</em> of text that has nice effects such as creative fills or that blends with its background in a nice subtle way. We used those images as a replacement for text on our pages, which made that text unaccessible and un-selectable.. But with all the advances in web design today, we can now create textured text effects using CSS, using SVG, and using HTML5 Canvas. This article introduces and shows you how to do that using all of those techniques.
</p>
Techniques For Responsive Typography
2013-11-11T00:00:00Z
https://sarasoueidan.com/blog/responsive-typography-techniques/
<p class="deck">
Text is the most important part of a website. Did you know that are several ways to make text responsive? Be it big headlines or body copy, the article will cover all those techniques, serving as an ultimate reference for making text responsive. We will cover accessibility, media queries, viewport units, and much more.
</p>
CSS Overlay Techniques
2013-11-07T00:00:00Z
https://sarasoueidan.com/blog/css-overlay-techniques/
<p class="deck">
Overlays can sometimes be annoying, but also undoubtedly have their useful use cases. There are different approaches to creating overlays, some of them work better than others, and some of them come with gotchas that you need to be aware of, including performance implications. In HTML5, we also get a native way to create modals with less hassle and less code. In this article, we will cover all of that.
</p>
Creating Non-Rectangular Layouts With CSS Shapes
2013-11-05T00:00:00Z
https://sarasoueidan.com/blog/css-shapes/
<p class="deck">Today we can create all kinds of <a href="https://css-tricks.com/examples/ShapesOfCSS/">shapes with CSS</a> using CSS transforms, but all these shapes do not affect the flow of the content inside or around them. That is, if you create a triangle or a trapezoid with CSS, for example, the shape created does not define or affect the way the text inside it flows, or the way inline text around it does.</p>
<p>With the introduction of CSS Shapes into the web, wrapping content in custom non-rectangular shapes, and recreating print designs and layouts on the web becomes a piece of cake!</p>
<p>In this article we're going to go over the basics of declaring shapes, and creating some simple layouts using these new CSS technologies. When more CSS Shapes features are implemented, more complex and awesome layouts will be possible, but even with what we have at hand now, <a href="https://blogs.adobe.com/webplatform/2013/10/23/css-shapes-visual-storytelling/">some interesting and very creative layouts</a> can be created with a little extra experimentation.</p>
<p> <strong>The CSS technologies we’ll be covering in this article may not work in your browser. If you want to see the working live demos you need to make sure you’re viewing them in a browser that supports these technologies. Check the <a href="https://caniuse.com/#feat=css-shapes">current state of browser support for CSS Shapes</a> out. You <em>don't need</em> a supporting browser to understand the features and demos, though. I've included screenshots of the demos so you can see how the final result looks like</strong>.</p>
<p class="note warning">
Most of this article's demos use the <code>shape-inside</code> property, which has been temporarily <a href="https://bugs.webkit.org/show_bug.cgi?id=130698">removed from Webkit</a> and <a href="https://codereview.chromium.org/209443007/">Blink</a>. So, for the time being, this article will only show screenshots of how the demos work when <code>shape-inside</code> is implemented again.
</p>
<h3 class="deeplink" id="declaring-shapes">Declaring Shapes</h3>
<p>All HTML elements have a rectangular box model which governs the flow of content inside and around it. In order to give an element a custom non-rectangular shape, the <code>shape-inside</code> and <code>shape-outside</code> properties are used. At the time of writing of this article, the <code>shape-outside</code> property can be applied to floating elements only, and the <code>shape-inside</code> property isn't completely implemented, so you may still find bugs when u use it. The shape-* properties can also only be applied to block-level elements. Non-block-level elements should be forced to block if you want to use a shape property on them.</p>
<p>Shape-* properties take one of three values: auto, a basic shape, or an image URI. If the value is set to auto, the element’s float area uses the margin box as normal. (If you’re not familiar with the <a href="https://www.w3.org/TR/2007/WD-css3-box-20070809/">CSS box model</a>, make sure you read up on it because you should know how it works). </p>
<p>If the value is set to a shape function, then the shape is computed based on the values of one of ‘<code>inset</code>’, ‘<code>circle</code>’, ‘<code>ellipse</code>’ or ‘<code>polygon</code>’. You can learn more about each of these functions in <a href="https://blogs.adobe.com/webplatform/2014/02/11/new-css-shapes-syntax/">this article</a> by the Adobe Platform team.</p>
<p> And finally, if the value is set to an image URI, the browser will use the image to extract and compute the shape based on the image’s alpha channel. The shape is computed to be the path that encloses the area where the opacity of the specified image is greater than the <code>shape-image-threshold</code> value. If the <code>shape-image-threshold</code> is not specified, the initial value to be considered is 0.5. The image should be CORS-same-origin, otherwise, it won't work, and the default value <code>auto</code> will be the value of the computed shape.</p>
<p>Shapes defined using the <code>shape-outside</code> property define the <em>exclusion area</em> on an element, while those defined using the <code>shape-inside</code> property define the <em>float area</em> of an element. We'll learn what each of these means in the examples below.</p>
<p>The shapes defined by the shape-* properties can be modified by using the <code>shape-margin</code> and <code>shape-padding</code> properties. The margin and padding shape properties are self-explanatory.</p>
<h3 class="deeplink" id="establishing-a-coordinate-system">Establishing a coordinate system on an element</h3>
<p>For the CSS shape declared to actually be applied on an element, we need to first start with establishing a coordinate system which we’ll be using to draw the shape.</p>
<p>A coordinate system is necessary because the shapes you declare will be defined by a set of points (and radii if you’re drawing circles or ellipses for example), and these points have x and y coordinates which will be placed on this coordinate system.</p>
<p>The shape-* properties use the content box of the element they’re applied to for their coordinate system, so in order to make them work, <strong>you need to specify a fixed width and height for the element</strong> which defines its bounding box, which in turn will be used to establish the coordinate system for the shapes you draw. <strong>If no explicit width and height are specified, the shape-* properties don’t work</strong>.</p>
<p>The origin of the coordinate system defined on the element's bounding box is positioned at the top left corner.</p>
<p>So, to declare a shape an element you have to start with:</p>
<ol>
<li>Specifying the dimensions of the element getting the shape (remember: the element should be floated when using <code>shape-outside</code> on it).</li>
<li>Declaring the shape on that element using the shape-* properties.</li>
</ol>
<h3 class="deeplink" id="applying-a-custom-shape">Applying a background to a custom shape</h3>
<blockquote class="quotes-left">
<p>While the boundaries used for wrapping inline flow content outside a float can be defined using shapes, <strong>the actual box model does not change</strong>. If the element has specified margins, borders or padding they will be computed and rendered according to the <a href="https://www.w3.org/TR/css-shapes/#CSS3BOX">CSS3BOX</a> module.
<cite>—<a href="https://www.w3.org/TR/css-shapes/">W3C CSS Shapes Module Level 1</a></cite>
</p>
</blockquote>
<p>In other words, the shape you define on an element using shape-* properties <em>only</em> affects the element’s float area, i.e. the flow of the content inside/outside this element, but all the element’s other properties won’t be affected.</p>
<p>For example, suppose you only want to draw a circular shape and have content float on its side like the shape in the image below, you’d first have to declare the circular shape on the element (again, remember to float the element and give it a height and width). Then, say you want to apply a background color to the circular shape to look like the one in the image..</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/shape-background.png" alt="Background applied to CSS shape" />
<figcaption>Background applied to a custom declared shape</figcaption>
</figure>
<p>You’d be tempted to just add a background color to the containing element and then end up with the above result (that’s what I did the first time), but doing that won’t do the job. The reason for that is that all properties of the element, other than the flow of content outside it, won’t be affected by the shape you defined inside it, and they will be rendered normally according to the element’s box model (its rectangular shape), as we’ve seen in the spec. So if you apply a background color to it, you’ll end up with this.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/box-model-background.png" alt="Background applied to rectangular box model" />
<figcaption>Background applied to the element's rectangular box shape</figcaption>
</figure>
<p>So, <em>how can we apply the color to the shape only and not the whole element?</em> This is where the <code>clip-path</code> property from <a href="https://www.w3.org/TR/2014/WD-css-masking-1-20140213/">the CSS Masking specification</a> can help.</p>
<p>The <code>clip-path</code> property will be used to <em>clip</em> parts of the element that we don’t need and keep only the parts inside the shape we defined. That obviously means that we’re not actually applying the color <em>to</em> the shape, we’re just <em>trimming</em> the element and leaving only the shape intact. With this, you’ll end up with a floating circle wrapping text outside it.</p>
<p><em>How, exactly? what value does the clip-path property get to do this?</em></p>
<p>The user coordinate system for the shapes defined by the <code>clip-path</code> property is established using the bounding box of the element to which the clipping path is applied, so the coordinate system is the same one as that of the shape-* properties.</p>
<p>Because of this, we can use the same shape defined in the shape-* property for the clip path, which will cut out, or <em>clip</em>, everything inside the containing element that’s outside the boundaries of the shape, and we’ll end up with a custom shape with a background.</p>
<p>You can test this concept live in <a href="https://codepen.io/SaraSoueidan/pen/ad12e1280e4b1c481faa3b82bd9a3263">this pen</a>, just make sure you test it in a supporting browser.</p>
<h3 class="deeplink" id="reminder">Quick Reminder</h3>
<p>At the time of writing of this article, the <code>shape-outside</code> property only works on floats, and both <code>shape-outside</code> and <code>shape-inside</code> properties are applied only to block-level elements, or inline elements <strong>forced to block</strong>. A shape defined on a float will cause inline content to wrap around the defined shape instead of the float's bounding box. Future levels of CSS Shapes will allow use of shapes on elements other than floats, and when that happens we’ll be able to wrap content on both sides of a shape (as in the image below). So for now, we can only float an element and have content flow on either side of it.</p>
<figure>
<img src="https://dev.w3.org/csswg/css-shapes-2/images/shapes_CSS2.1_MBP.png" alt="Example rendering of circle shape and box model." />
<figcaption>Flowing content on both sides of a CSS shape</figcaption>
</figure>
<p>You could also fake wrapping content on both sides using the <a href="https://betravis.github.io/shape-tools/exclusion-punch/">Exclusion Punch plugin</a> by <a href="https://twitter.com/bear_travis">Bear Travis</a>.</p>
<p>Now let’s get our hands dirty drawing some shapes and creating some fun layouts!</p>
<p>Each of the following examples will introduce a new tip/idea/technique that are used to define and use CSS shapes and exclusions.</p>
<p><strong>You can view the live demo for each example by clicking on the demo's screenshot.</strong></p>
<h3 class="deeplink" id="example-1">Example #1: Floating text around a custom shape with <code>shape-outside</code></h3>
<p>We’ll start with a simple example. In this example we’re going to define a custom shape and have content flow on its side. The end result will look like the image below:</p>
<a href="https://sarasoueidan.com/demos/css-shapes-layouts/demo-1/index.html">
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-1/images/demo-screenshot.jpg" alt="Screenshot of Demo #1" />
<figcaption>Screenshot of Demo #1. Click on the screenshot to see the working demo.</figcaption>
</figure>
</a>
<p>In the demo we have a container which contains two elements: a <code>.content</code> container with text on the left, and another element with a class<code>.shaped</code> floated to the right, which will get the custom shape and have the text flow on its left side.</p>
<p>The heading in the <code>.content</code> area is also getting a similar treatment to the one we're giving the floated div on the right, so I'll skip its explanation and only talk about what we're doing on the <code>.shaped</code> area on the right.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shaped<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>La<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> Tour <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">/></span></span>Eiffel<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Lorem Ipsum......<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>We will first start by giving the floated <code>div</code> on the right a specific height and width to establish a coordinate system. We’ll set its height to be the same as its container, which for this demo I’ve set to be the same height as the viewport, using CSS’s <code>vh</code> unit.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span>hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100vw<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.shaped</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span>left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span>100vh<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>40vw<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span>right<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> black <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>../images/eiffel.jpg<span class="token punctuation">)</span></span> center top no-repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span>cover<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now that the coordinate system is ready, we’re going to draw the shape, to define the float and exclusion areas of the element. There are two ways to go about declaring a shape for this demo:</p>
<h4 class="deeplink" id="using-polygon">Using <code>polygon()</code></h4>
<p>For the first method, we’ll be using the polygon() function. This function takes in a set of points that form the polygon, each point defined by x and y coordinates. We're going to define a very simple polygonal shape, with 4 vertices, as shown in the image below (blue and orange discs):</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-1/images/demo-shape.jpg" alt="Vertices of the Polygon" />
<figcaption>Screenshot showing the vertices making up the polygonal shape</figcaption>
</figure>
<p>The coordinates of the points can have either specific values (px or em), or percentage values. In this example we're going to provide percentage values for the vertices visible in the above screenshot. Now all we have to do is just declare this shape on the floated element so that the text flows on its side.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.shaped</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/*...*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-outside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 100% 0<span class="token punctuation">,</span> 100% 100%<span class="token punctuation">,</span> 30% 100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-margin</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And that’s it! the text can now flow in the <em>float area</em> of the element, defined by the custom shape we declared on it.</p>
<p>You can also see that I've added a margin to the shape, to push the content away from the shape a little and create a gap.</p>
<p>But we have one more thing to add here. Like I mentioned in a previous section, the background of the floated element is applied to its original rectangular shape, not just to the shape we declared on it, because the background property is not affected by the shape declared on the element. So far, the demo looks like this:</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-1/images/demo-screenshot-incomplete.jpg" alt="Screenshot of background applied to rectangular shape of the element" />
<figcaption>Screenshot showing the background applied to the element covering its rectangular shape</figcaption>
</figure>
<p>So in order to clip out the excess areas that we don't need, we're going to use the <code>clip-path</code> property, and give it the same value/shape that we gave to the <code>shape-outside</code> property above. So we add this rule to the rule set:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.shaped</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/*...*/</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 100% 0<span class="token punctuation">,</span> 100% 100%<span class="token punctuation">,</span> 30% 100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And we're done! Simple, right? </p>
<p>The page title on the left gets the same treatment as the <code>.floated</code> <code>div</code> on the right. The heading is floated inside its container <code>.content</code>, it is given a specific height and width to establish a coordinate system, and then a shape is declared on it using the <code>shape-outside</code> property just like we did on the <code>.floated</code> element.</p>
<h4 class="deeplink" id="using-an-image-uri">Using an image URI</h4>
<p>Another way we could define the shape on our element is by using an image with an alpha channel, that is, any image with transparent areas.</p>
<p>For our example here, instead of using the polygon() function to define the shape, we’ll give the <code>shape-outside</code> property an image URI, and the browser will extract the shape from the image, and use it.</p>
<p>The image that would define the exclusion area for this example is the one shown below. You can see that the image shows the same shape defined by the polygon() points in the previous method.</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-1/images/mask.png" alt="Image with Alpha Channel showing the polygonal shape" />
<figcaption>Image with Alpha Channel whose URI will be used to extract and compute the value of the shape</figcaption>
</figure>
<p>When you’re using an image with alpha channels to define a shape <strong>for the shape-outside property</strong>, the <em>transparent area</em> of the image will define the area where the inline text flows, this is the area called the <em>float area</em> of the element. The black portion defines the exclusion area of the element.</p>
<p>To use this image we write the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.shaped</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/*...*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-outside</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>../images/mm.png<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-image-threshold</span><span class="token punctuation">:</span> 0.5<span class="token punctuation">;</span><span class="token comment">/* this property is used to set the threshold used for extracting a shape from an image. 0.0 = fully transparent and 1.0 = fully opaque */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Each of the two methods mentioned has its advantages. You might want to use an image URI for complex shapes that may be cumbersome to define the points for manually, in this case creating an alpha channel image in Photoshop would be much easier and faster than manually adding the points.</p>
<p>Another situation where you might want to use an image URI instead of a shape function is when you have multiple float or exclusion areas inside an element, in that case using this method is necessary because you can’t, for now, declare multiple shapes on an element, but if the image contains multiple areas, the browser will extract these areas from the image and use them. Pretty neat, right? :) we’ll see an example of this in the last demo.</p>
<h3 class="deeplink" id="example-2">Exmaple #2: wrapping/flowing text inside a custom shape with <code>shape-inside</code></h3>
<p>For the second example we’ll create a simple demo where the end result will look like this:</p>
<a href="https://sarasoueidan.com/demos/css-shapes-layouts/demo-2/index.html" class="image-link">
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-2/images/demo-screenshot.png" alt="Screenshot of demo #2" />
<figcaption>Screenshot of demo #2</figcaption>
</figure>
</a>
<p>The goal of this example is to demonstrate the <code>shape-inside</code> property used to float text inside a non-rectangular shape. We have a container element with some placeholder text inside it, and we applied the photo as a background image to this container.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Corn Bread<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>As you can see from the demo screenshot above, the text is wrapped inside a circular shape at the top. So, we know that we’re going to have to declare a circle on our container. Now, like in the previous example, there are two ways we can do that..</p>
<h4 class="deeplink" id="using-circle">Using <code>circle()</code></h4>
<p>Using the <code>circle()</code> function we're going to define a circle and position it on our element.</p>
<p>The image below shows the coordinate system established on the element, and the position of the circle inside the element. We’re making sure the circle is positioned on top of the pan image inside the photo we’re using as a background, so that it appears as if the text is contained inside that pan. On the image the position of the center of the circle with respect to the coordinate system established on the element is also visible.</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-2/images/demo-shape.png" alt="Coordinate system and shape defined on the container" />
<figcaption>Coordinate system and shape defined on the container</figcaption>
</figure>
<p>Because we want to wrap text <em>inside</em> a custom shape, and not flow it around it, we’re going to use the <code>shape-inside</code> property on the element containing this text. When you're applying the <code> shape-inside</code> property to an element, you have to remember that this element would have the text content inside it, unlike the previous example, where the content was outside the element we declared the shape on.</p>
<p> We’ll specify the coordinates of the center of the circle and we'll set the value of its radius, and apply those to the container:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">float</span><span class="token punctuation">:</span>left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>600px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span>900px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span>hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span>0 50px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span>white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span>13px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span>10px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>../images/pan.jpg<span class="token punctuation">)</span></span> top left no-repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span>100% 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/*declare shape using the shape function circle()*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>160px at 400px 60px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Of course, unless you're attempting to create a perfect circular shape, you can also define the shape using <code>polygon()</code>.</p>
<h4>Using an image URI</h4>
<p>We can also use the URI of an image with an alpha channel to extract the shape of the circle from it. The image would look like the following: </p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-2/images/mask.png" alt="Image with Alpha Channel showing the circular shape
" />
<figcaption>Image with Alpha Channel defining the circular shape</figcaption>
</figure>
<p> It’s important to note here that when you’re using an image with an alpha channel to define a shape <strong>for the <code>shape-inside</code> property</strong>, the <em>black (or opaque) area</em> of the image will define the area where the text flows. In the previous example, the opaque area defined the <em>exclusion area</em> of the element we applied the shape to, i.e the area where <strong>no</strong> text flows.</p>
<p>So declare the shape using an image URI instead of the shape function <code>circle()</code>, you'll have to set the value of the <code>shape-inside</code> property to point to the URI of the image:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/*...*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>mask.png<span class="token punctuation">)</span></span> top left<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 class="deeplink" id="example-3">Example #3 : wrapping/flowing text inside a custom shape with <code>shape-inside</code></h3>
<p>In this example we're also going to declare a polygonal shape on a container and have its content flow inside this shape. The end result will look like the image below:</p>
<!-- <a href="../../demos/css-shapes-layouts/demo-3/index.html" class="image-link"> -->
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-3/images/demo-screenshot.png" alt="Screenshot of Demo #3" />
<figcaption>Screenshot of Demo #3</figcaption>
</figure>
<!-- </a> -->
<p>Here, too, we can use either a shape function or an image URI to declare the shape on the element.</p>
<p>The shape declared on this container is clearly a "random" polygonal shape, not a geometric shape that we could declare using a shape function like <code>circle()</code>, <code>ellipse()</code>, or <code>inset()</code>, so we're going to use the <code>polygon()</code> function to declare it.</p>
<p>The shape defined by a set of points is visible in the image below.</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-3/images/demo-shape.png" alt="The polygonal shape defined by a set of points" />
<figcaption>The polygonal shape defined by a set of points</figcaption>
</figure>
<p>Because there's a fairly large number of points making this shape up, it would be cumbersome to calculate the coordinates of these points, so it would be helpful if there was a <strong> visual</strong> tool available to help us <em>plot</em> these points on the image, right? Well, there is a tool created by Adobe's <a href="https://twitter.com/bear_travis">Bear Travis</a>, which is actually a collection of tools that can help you when working with CSS shapes. Make sure you <a href="https://betravis.github.io/shape-tools"> check the Shape Tools out</a> because they are very valuable.</p>
<p>One of the Shape tools mentioned is called <a href="https://betravis.github.io/shape-tools/polygon-drawing/">Poly Draw</a>, and it allows you to manually "draw" a shape, a polygon in particular, and then it generates the coordinates of the shape for you to copy and paste into your CSS to declare the shape on your element.</p>
<p>I have used the Poly Draw tool to draw the above shape on the image. Now, the tool does not take an image and sets it as a background for the element you define the shape on, so I had to git clone the repo of the tool and fiddle with the tool’s code a bit in the dev tools, and I applied the image to it and plotted the points on it.</p>
<p><a href="https://twitter.com/razvancaliman">Razvan Caliman</a> suggested this idea when I asked him about the availability of a tool that allows us to define shapes on top of images right in the browser, just like the one he showed and used in <a href="https://www.youtube.com/watch?v=zsLwZhTSuQk&list=PL8rji95IPUUDu3puqqxWMKFXf-NQ4z7WE&index=11">his talk at this year's CSS Conf EU</a>. If you haven't watched his talk yet, make sure you do. The tool he used will some day, soon I hope, be open-sourced by Adobe, and then it'll be an indispensible tool when working with CSS shapes. But until then, you could do with the Poly Draw tool.</p>
<p>After drawing the shape with the Poly Draw tool, all you have to do is declare the resulting shape on your element and you're good to go.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>445px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span>670px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span>hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span>30px auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/*shape generated by the Poly Draw tool*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-outside</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>170.67px 291.00px<span class="token punctuation">,</span>126.23px 347.56px<span class="token punctuation">,</span>139.79px 417.11px<span class="token punctuation">,</span>208.92px 466.22px<span class="token punctuation">,</span>302.50px 482.97px<span class="token punctuation">,</span>343.67px 474.47px<span class="token punctuation">,</span>446.33px 452.00px<span class="token punctuation">,</span>443.63px 246.82px<span class="token punctuation">,</span>389.92px 245.63px<span class="token punctuation">,</span>336.50px 235.26px<span class="token punctuation">,</span>299.67px 196.53px<span class="token punctuation">,</span>259.33px 209.53px<span class="token punctuation">,</span>217.00px 254.76px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>We could also define the shape above using an image with an alpha channel. The image below shows what that image would look like. Again, the black areas define the float area when using <code>shape-inside</code>, and they're where the text is going to flow.</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-3/images/mask.png" alt="Image with alpha channel defining the shape for demo #2" />
<figcaption>Image with alpha channel defining the shape for demo #2</figcaption>
</figure>
<p>If you want to go with the image URI instead of the shape function, youd replace the above shape outside value with the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/*...*/</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>mask.png<span class="token punctuation">)</span></span> top left<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 class="deeplink" id="example-4">Example #4 : Multiple float areas with <code>shape-inside</code></h3>
<p>In this example we're going to create multiple float areas inside an element to wrap content inside. The result of this demo is shown in the following image:</p>
<!-- <a href="../../demos/css-shapes-layouts/demo-4/index.html" class="image-link"> -->
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-4/images/demo-screenshot.jpg" alt="Screenshot of Demo #3" />
<figcaption>Screenshot of Demo #4</figcaption>
</figure>
<!-- </a> -->
<p>We have a <code>div</code> with a background image, and we want the text inside this <code>div</code> to flow inside specific areas inside it, all of which have custom shapes.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Rosemary Sandwich<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, since we can't declare multiple shapes on an element, we're going to use an image with an alpha channel. An image can contain as many shapes and areas as you want, so it's perfect to define multiple shapes on an element, and the browser will extract all the shapes from this image and use them on the element.</p>
<p>We'll use the following image to define the shapes. The black areas in the image will define the float area of for the content inside the <code>.container</code> where the text will flow.</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-4/images/mask.png" alt="Image with Alpha Channel defining shapes for demo #3" />
<figcaption>Image with Alpha Channel defining shapes for demo #3</figcaption>
</figure>
<p>We'll use the URI of this image as a value for the <code>shape-inside</code> property that we're going to declare on the <code>.container</code>, all the while remembering to set height and width values for the <code>div</code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.container</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>556px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span>835px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span>hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span>0 50px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span>white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span>relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>../images/bread.jpg<span class="token punctuation">)</span></span> top left no-repeat<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">shape-inside</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>mask.png<span class="token punctuation">)</span></span> top left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span>13px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And we're done. The browser does the rest of the work for us by extracting the shapes from the image we gave it, and our text flows nicely inside those areas!</p>
<p>Using an image to define the shapes is the logical way to go when you have separate areas that are not connected to eachother, i.e that don't form a singe polygonal shape. For this demo, we could have used the <code>polygon()</code> function to define the shape, by defining a polygon that looks like the one in the image below:</p>
<figure>
<img src="https://sarasoueidan.com/demos/css-shapes-layouts/demo-4/images/demo-shape.png" alt="Image of shape defined using polygon()" />
<figcaption>Image representing the points used to define a single polygon</figcaption>
</figure>
<p>But, as you can notice, this isn't the best way to do this, I just added this to show the difference between using an image and defining the shape with <code>polygon()</code>, and to show that sometimes the best practice or the one that seems more proper and makes more sense is to use an image, even if you can use a shape function to define your shapes.</p>
<h3 class="deeplink" id="shapes-with-regions-and-flexbox">Combining CSS Shapes with Regions and Flexbox to create magazine layouts</h3>
<p>Typical print magazines usually combine multi-column text layouts with non-rectangular shapes to create creative and appealing designs. The columns are usually equal in height unless needed otherwise.</p>
<p>Once future CSS Shapes features are implemented, and wrapping content on both sides of a shape is possible, creating print-like digital magazine designs becomes very much possible when combining Shapes and Exclusions with Regions and Flexbox.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/multicolumn-shapes.png" alt="Multi-column layouts with shapes" />
<figcaption> Travel Magazine by Bartosz Kwiecień on Behance. Layout like this could be replicated using future CSS Shapes technologies and Regions (<a href="https://www.behance.net/gallery/Travel-Magazine/2159303"></a>) </figcaption>
</figure>
<p>Flexbox provides us with the equal-height columns, Regions allows us to flow text into different areas on the page and separate the page content from its layout, and Shapes and Exclusions will allow us to add that final creative touch that takes our magazine layouts to the next level.</p>
<h3 class="deeplink" id="final-words">Final Words</h3>
<p>I don’t think I’ve been excited about a new CSS feature as I am about CSS shapes and exclusions. The power, flexibility, and creativity that these features combined regions and flexbox can provide is just fantastic!</p>
<p>Widespread support for CSS Shapes should be coming soon, as the web platform team at Adobe is constantly working on improving and implementing these features, and providing tools to make working with them easier.</p>
<p>The future of web layout is looking brighter and more captivating every day. It's a wonderful time to be a web developer!</p>
<p>I hope this article helped introduce you more to the technical part of getting started with CSS Shapes. This will not be my last article on this topic. Combining CSS Shapes with other cutting edge CSS technologies like Regions opens the door to a new world of creativity, and lots of new tutorials! ;)</p>
<p>You should subscribe to my blog's <a href="https://feeds.feedburner.com/sarasoueidan">RSS feed</a> and <a href="https://twitter.com/SaraSoueidan">follow me on Twitter</a> to stay in the loop for upcoming new articles.</p>
<p>Thank you for reading!</p>
<h4>Resources & Further Learning</h4>
<ul class="resources">
<li>Bear Travis’s <a href="https://betravis.github.io/shape-tools/">CSS Shape tools</a> </li>
<li>W3C's <a href="https://www.w3.org/TR/css-shapes/">CSS Shapes Working Draft</a></li>
<li>CSSWG Wiki on <a href="https://wiki.csswg.org/ideas/css3-exclusions-use-cases">CSS Shapes and Exclusions use cases examples</a></li>
<li>Adobe’s <a href="https://html.adobe.com/webplatform/layout/shapes/browser-support/">CSS shapes support matrix</a></li>
<li>Adobe Web Platform's <a href="https://html.adobe.com/webplatform/layout/shapes/">resources for CSS Layout</a></li>
<li><a href="https://github.com/betravis/web-layout-lab">This project</a> by Bear Travis contains a series of exercises demonstrating new web platform layout features including an combining CSS Flexbox, Grid, Regions, Shapes, and Exclusions.</li>
<li><a href="https://galjot.si/css-exclusions">CSS Exculsions article</a> by<a href="https://galjot.si/"> Robert Sedovše</a> </li>
</ul>
<p><em>This article wouldn’t have been possible without the great help from <a href="https://razvancaliman.com/">Razvan Caliman</a>, so a big thanks goes to him.</em></p>
Navicon Transformicons: Animated Navigation Icons with CSS Transforms
2013-10-20T00:00:00Z
https://sarasoueidan.com/blog/navicon-transformicons/
<p class="deck note">The following is a collaboration post between <a href="http://bennettfeely.com/">Bennett Feely</a> and I. After seeing Bennett's impressive animated navigation icon transformations (or "Navicon Transformicons") <a href="http://codepen.io/bennettfeely/pen/twbyA">pens</a> <a href="http://codepen.io/bennettfeely/pen/eClzu">on Codepen</a>, I asked him if he would like to write a tutorial on how he did them as a guest post on my blog. He kindly approved. And as he doesn't have a lot of free time to work the article, we decided to collaborate on it. We'll be covering a few of the icons he created in his pen, and a couple more.</p>
<p>If you were to ask me what my most favorite CSS property is I might just answer the transition property. It has proven to be a perfect use case for progressive enhancement and it’s adoption has made countless websites feel smoother. By the way, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties">a heck of a lot of properties</a> are also transitionable. </p>
<p>While the prefixed transition property has been supported by the major browsers for a long time now (web speaking), there was quite a dilemma with browsers and their ability to transition and animate pseudo-elements (<code>:before</code> and <code>:after</code>). While Firefox has been doing things right since version 4.0, it wasn’t until March of this year when Chrome finally <a href="http://css-tricks.com/pseudo-element-animationstransitions-bug-fixed-in-webkit/">fixed things</a>. Now, even IE10 supports transitions and animations on pseudo-elements.</p>
<p>So what shall we call these transforming CSS icons? How about transformicons?</p>
<p class="note warning">
These code snippets are intended to work only in <a href="http://caniuse.com/#feat=transforms2d">browsers that support</a> the properties used.
</p>
<p class="note">
We will only cover the styles/transforms in SCSS, and add some explanation in the form of comments for those of you who aren't very familiar with SCSS. You'll find the complete compiled CSS code in the source code on Github.<br /> As the Javascript is very simple (just toggling a class name) we won't be going over it, and you'll also find it in the downloadable source code on Github.
</p>
<h3 class="deeplink" id="demo-1">Three-line to arrow (arrow left and arrow up)</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/lines-to-arrows.png" alt="Three-lines to Arrows Transformations" />
</figure>
<h4 class="deeplink" id="demo-1-markup">The Markup</h4>
<p>The three-line menu icon, aka navicon, aka hamburger icon can be accomplished quite a few different ways, but in this case we will use a wrapper element and a child with two psuedo elements to form the three lines. The markup really isn’t heavy.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lines-button arrow arrow-left<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Toggle Navigation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lines<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h4 class="deeplink" id="demo-1-scss">The SCSS</h4>
<p>First we’ll set up the wrapper around the actual navicon to trigger the transition. <code>$button-size</code> is the width of the lines of the navicon, not the entire target area.</p>
<pre class="language-scss"><code class="language-scss"><span class="highlight-line"> <span class="token property"><span class="token variable">$button-size</span></span> <span class="token punctuation">:</span> 3.5rem<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property"><span class="token variable">$transition</span></span><span class="token punctuation">:</span> .3s<span class="token punctuation">;</span> <span class="token comment">// increase this to see the transformations in slow-motion</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">.lines-button </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/2 <span class="token variable">$button-size</span>/4<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> .3s<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">user-select</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/7<span class="token punctuation">;</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:hover </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:active </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>.1<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>And now a mixin that we will use to make a single line.</p>
<pre class="language-scss"><code class="language-scss"><span class="highlight-line"> <span class="token keyword">@mixin</span> <span class="token selector">line </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/7<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token variable">$color</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/14<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> <span class="token variable">$transition</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>We are using the mixin in the <code>.lines</code> element and its absolutely positioned pseudo elements to create the navicon. </p>
<pre class="language-scss"><code class="language-scss"><span class="highlight-line"> <span class="token selector">.lines </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token comment">//create middle line</span></span><br /><span class="highlight-line"> <span class="token keyword">@include</span> line<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">/*create the upper and lower lines as pseudo-elements of the middle line*/</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:before, <span class="token parent important">&</span>:after </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token keyword">@include</span> line<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span>0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transform-origin</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/14 center<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:before </span><span class="token punctuation">{</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/4<span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:after </span><span class="token punctuation">{</span> <span class="token property">top</span><span class="token punctuation">:</span> -<span class="token variable">$button-size</span>/4<span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>We need to line up the transform origin of the pseudo elements (upper and lower lines) carefully if we want everything to line up perfectly.</p>
<p>I created a simple pen to show where the transform origin goes and how the pseudo-elements are transformed.<code>:before</code> is red, <code>:after</code> is blue, and <code>.lines</code> is green.</p>
<p>Check the pen out <a href="http://codepen.io/bennettfeely/pen/mhwDt">here</a>.</p>
<p>And here's a simple image to show the transform origins and how the pseudo-elements should align.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/transform-origin.png" alt="Image showing the transform origins of the pseudo-elements" />
</figure>
<p>When we hover over the three-line menu button in it’s original state, we’ll have it expand a little.</p>
<pre class="language-scss"><code class="language-scss"><span class="highlight-line"> <span class="token selector">.lines-button:hover </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector">.lines </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:before </span><span class="token punctuation">{</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/3.5<span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:after </span><span class="token punctuation">{</span> <span class="token property">top</span><span class="token punctuation">:</span> -<span class="token variable">$button-size</span>/3.5<span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>Finally, let’s transform this three-line menu into a left arrow icon. For this demo, when the <code>.lines-button</code> wrapper clicked, we will add a <code>.close</code> class to it. The arrow looks better when it is scaled down a bit so we will do so using <code>scale3d()</code> rather than just <code>scale()</code>, which will trigger hardware acceleration and should make things run a bit smoother.</p>
<pre class="language-scss"><code class="language-scss"><span class="highlight-line"> <span class="token selector">.lines-button.arrow.close </span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale3d</span><span class="token punctuation">(</span>.8<span class="token punctuation">,</span>.8<span class="token punctuation">,</span>.8<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">.lines-button.arrow.close .lines</span><span class="token punctuation">{</span></span><br /> <span class="token selector"><span class="token parent important">&</span>:before,<br /> <span class="token parent important">&</span>:after </span><span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token variable">$button-size</span>/1.8<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:before </span><span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>1<span class="token punctuation">,</span>40deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector"><span class="token parent important">&</span>:after </span><span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>1<span class="token punctuation">,</span>-40deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>For the <code>:before</code> and <code>:after</code> lines, we will shorten them a bit and overlay them all on top of each other. Finally, we rotate them 40° in opposite directions to each other. We have made an arrow!</p>
<p>For the <strong>second navicon transformation into an arrow pointing upwards</strong>, the markup remains the same, we’ll just add a class of <code>.arrow-up</code> to the button. </p>
<pre class="brush:html">
<button class="lines-button arrow arrow-up" type="button" role="button" aria-label="Toggle Navigation">
<span class="lines"></span>
</button>
```
<p>This icon will get the exact same styles and transformations as the previous one, but we'll rotate the icon in it's <code>.close</code> state by 90 degrees so the arrow points upwards.</p>
```scss
.lines-button.arrow-up.close {
transform: scale3d(.8,.8,.8) rotate3d(0,0,1,90deg); // Rotate around the z-axis
}
```
<h3 class="deeplink" id="demo-2">Three-line to —</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/lines-to-minus.png" alt="Three-lines to Minus Transformation" />
</figure>
<p>The markup for this one is, of course, the same as the markup in the previous section. The button in this example gets a <code>.minus</code> class, which defines the styles that will be applied to it.</p>
<h4 class="deeplink" id="demo-2-scss">The SCSS</h4>
<p>To style this icon we’ll apply the same styles as above too down until the hover state. But where this icon will differ from the previous one is in the styles applied to it when it’s clicked, i.e in the <code>.close</code> state. This icon will transform into a “—” (like a minus sign), which can resemble a “collapse menu” icon, or “show less”, if you’re using it for a mobile navigation. The pseudo-elements (top and bottom lines) won’t be rotated so we’ll reset the transforms to none, and we’ll keep the width of the icon instead of shrinking it, and then we'll just overlay them on top of the <code>.lines</code> element to form one single line instead of three.</p>
```scss
.lines-button.minus.close .lines{
&:before, &:after{
transform: none;
width: $button-size;
top:0;
}
}
```
<h3 class="deeplink" id="demo-3">Three-line to ✕ (#1)</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/lines-to-x.png" alt="Three-lines to x Transformation" />
</figure>
<p>This icon will start out the exact same way the previous ones have. The markup structure is the same as the previous three-lines icons, with the same hover state expanding effect. For this transformation, the icon will get a <code>.x</code> class (resembles a transformation to an x shape).</p>
<p>When the button is clicked, an <code>.close</code> class is added to it just like in the previous examples. But this is where the new transformation will be defined.</p>
<h4 class="deeplink" id="demo-3-scss">The SCSS</h4>
<p>In order to transform the three lines into an ✕ shape, we're going to change the icon's background into a transparent one (the middle line will disappear), and the upper and lower lines (the pseudo-elements) will be rotated by 45 degrees in opposite directions and overlayed to create the shape.</p>
```scss
.lines-button.x.close .lines{
/*hide the middle line*/
background: transparent;
/*overlay the lines by setting both their top values to 0*/
&:before, &:after{
transform-origin: 50% 50%;
top:0;
width: $button-size;
}
// rotate the lines to form the x shape
&:before{
transform: rotate3d(0,0,1,45deg);
}
&:after{
transform: rotate3d(0,0,1,-45deg);
}
}
```
<p>This transformation is very similar to the arrow transformation, but the key notes which make it different is keeping the width of the lines here instead of shrinking them like we did for the arrows, and keeping the transform origin at the center.</p>
<h3 class="deeplink" id="demo-4">Three-line to ✕ (#2)</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/lines-to-x.png" alt="Three-lines to x Transformation" />
</figure>
<p>This transformation is inspired by the fifth transformation style from Pedro Campos’s <a href="http://codepen.io/pedrocampos/details/gufrx">pen</a> on Codepen. We’ll make the markup for this one, of course, the same as the markup for all our buttons, with a specific class, in this case .x2.</p>
<h4 class="deeplink" id="demo-4-scss">The SCSS</h4>
<p>This icon will start out with the same transformation as the three-line-to-minus icon, and when the first transformation is finished, the pseudo-elements will rotate and form the ✕ shape. We’ll apply the second transformation when the first one is finished, so for that we’ll need to set a delay for the transitions so that they don’t happen simultaneously.</p>
<p>Where this transformation differs from the previous ✕ effect is the order of transformations and the added delays. For the previous effect we rotated and overlayed simultaneously, while in this case we're going to overlay, and delay the rotation till the overlaying is done, and then we'll rotate.</p>
```scss
.lines-button.x2 .lines{
transition: background .3s .6s ease;
&:before, &:after{
//set transform origin back to center
transform-origin: 50% 50%;
transition: top .3s .6s ease, transform .3s ease;
}
}
```
<p>We have added a delay on the transition for the lines so that the transformations happen in a row.</p>
<p>Next, we’ll define the transition delays and transformations for the pseudo-elements. When the button is clicked, the upper and lower lines will first be translated to overlay on top of each other, the middle line’s background will be set to transparent to hide it, because we don’t want it to be there when the x is formed, and then each of two remaining lines will be rotated by 45deg (and -45deg for the opposite line) to form an ✕ shape.</p>
```scss
.lines-button.x2.close .lines{
transition: background .3s 0s ease;
background: transparent;
&:before, &:after{
transition: top .3s ease, transform .3s .5s ease;
top:0;
width: $button-size;
}
&:before{
transform: rotate3d(0,0,1,45deg);
}
&:after{
transform: rotate3d(0,0,1,-45deg);
}
}
```
<p>The trick here that’s different from the previous transformations is just to set the transform origin of the pseudo-elements to be their center, and add the proper transition delays.</p>
<h3 class="deeplink" id="demo-5">Grid to ✖ (#1)</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/grid-to-x.png" alt="Grid to x Transformation" />
</figure>
<h4 class="deeplink" id="demo-5-markup">The Markup</h4>
<p>Similar to the previous markup, we have a <code>.grid-button</code> wrapping a <code>.grid</code> icon.</p>
<pre class="brush:html">
<button class="grid-button rearrange" type="button" role="button" aria-label="Toggle Navigation">
<span class="grid"></span>
</button>
```
<h4 class="deeplink" id="demo-5-scss">The SCSS</h4>
<p>For this icon, instead of using psuedo elements we will instead leverage the power of the mighty <code>box-shadow</code> property. To make the code cleaner and easier to modify, we will create a <code>$base</code> and a <code>$space</code> variables. First we will style the <code>.grid-button</code>, wrapper.</p>
```scss
//variables are used to make the buttons more flexible and easier to customize
//these variables are replaced with their values in the compiled CSS
$base : 1rem;
$space : $base/4;
$color : #c0392b;
.grid-button {
padding: $base*2; //2rem
cursor: pointer;
user-select: none;
}
```
<p>Now let’s get to the <code>.grid</code> icon itself and the crazy <code>box-shadow</code> property. Think of each comma-separated shadow as a it’s own sort of pseudo- element. It is very important to keep track of the order of each shadow in the box-shadow property or the animation will not look right.</p>
<p>The <code>box-shadow</code> property is nice that when a color is not specified, the property simply inherits whatever the color property may be. In a situation like ours, it’s very helpful with an element with a ton of shadows that are the same color to simply leave out the colors and set it once with the color property.</p>
```scss
.grid-button .grid{
width: $base;
height: $base;
background: $color;
color: $color; /* Not in use when the colors are specified below */
transition: $transition;
}
```
<p>When we click on the button, we add the <code>.close</code> class to <code>.grid-button</code>.</p>
<p>Because we'll be using two techniques to create an ✖ out of the grid icon, we'll be using two different class names for two two transformations. For the first one we'll use a <code>.rearrange</code> class name, as we'll be rearranging the box shadows.</p>
<p>First we’ll spread the box shadows for the icon to form a grid.</p>
```scss
.grid-button.rearrange .grid{
box-shadow:
-($base+$space) 0 -($base+$space),
0 0 -($base+$space),
($base+$space) (-($base + $space)),
-($base+$space) 0,
$base+$space 0,
-($base+$space) ($base + $space),
0 ($base+$space),
($base+$space) ($base + $space);
}
/* The CSS equivalent to the above box-shadow result is:
box-shadow:
-1.25rem -1.25rem,
0 -1.25rem,
1.25rem -1.25rem,
-1.25rem 0,
1.25rem 0,
-1.25rem 1.25rem,
0 1.25rem,
1.25rem 1.25rem;
*/
```
<p>And when the icon gets the <code>.close</code> class on click, we’ll rearrange the shadows.</p>
```scss
.grid-button.rearrange.close .grid{
box-shadow:
0 0 -$base,
0 0 -$base*2,
$base 0,
-$base*2 0,
$base*2 0,
-$base 0,
0 $base*2,
0 $base;
transform: rotate3d(0,0,1,-45deg) scale3d(.8,.8,.8);
}
/* The CSS equivalent to the box-shadow is:
box-shadow:
0 -1rem,
0 -2rem,
1rem 0,
-2rem 0,
2rem 0,
-1rem 0,
0 2rem,
0 1rem;
*/
```
<p>We have removed all the spaces between the individual shadows (removed all the <code>$space</code> variables), and moved the four corner shadows inward and four side shadows outward by rearranging them. Last but not least, we rotate the whole icon by -45° and scale it, all using hardware acceleration to make the animation run smoothly. And with that we've achieved the first effect.</p>
<h3 class="deeplink" id="demo-6">Grid to ✖ (#2)</h3>
<figure>
<img src="https://sarasoueidan.com/assets/images/grid-to-x-2.png" alt="Grid to x Transformation #2" />
</figure>
<p>For the second grid to ✖ transformation, we’ll be doing something very similar to what we did previously, but instead of rearranging the shadows we’re going to “collapse” four of them into the main element and bring the other four closer by removing the spaces and thus forming an ✖. We'll give the button with this effect a class <code>.collapse</code>.</p>
```scss
.grid-button.collapse .grid{
//the order of box shadows here differs a little from the above one order-wise
box-shadow:
-($base+$space) 0,
-($base+$space) ($base+$space),
$base+$space 0,
($base+$space) (-($base+$space)),
0 0 -($base+$space),
-($base+$space) 0 -($base+$space),
0 ($base+$space),
($base+$space) ($base+$space);
}
/*The CSS equivalent to the box-shadow specified here is:
box-shadow:
-1.25rem 0,
-1.25rem 1.25rem,
1.25rem 0,
1.25rem -1.25rem,
0 -1.25rem,
-1.25rem -1.25rem,
0 1.25rem,
1.25rem 1.25rem;
*/
```
<p>And when the button is clicked the <code>.close</code> class is added, and the shadows “collapse”.</p>
```scss
.grid-button.collapse.close .grid{
box-shadow:
-$base 0,
0 0 transparent,
$base 0,
0 0 transparent,
0 0 -$base,
0 0 transparent,
0 $base,
0 0 transparent;
}
/*The CSS equivalent to the box-shadow result is:
box-shadow:
-1rem 0,
0 0 transparent,
1rem 0,
0 0 transparent,
0 -1rem,
0 0 transparent,
0 1rem,
0 0 transparent;
*/
```
<p>And we're done! I hope you liked these effects and found the tutorial useful!</p>
</pre></pre>
Building A Circular Navigation With CSS Transforms
2013-08-09T00:00:00Z
https://sarasoueidan.com/blog/circular-navigation/
<p class="deck">
In this article, we will learn how to apply CSS transforms to fake a "slice" shape, creating a circular navigation using nothing but CSS (and some maths!). The article includes an interactive demo that explains visually and step by step how the technique works and the shapes are created.
</p>
Lessons from the “Seductive Interaction Design” Book
2013-04-26T00:00:00Z
https://sarasoueidan.com/blog/lessons-from-seductive-interaction-design-book/
<p class="deck">
In this article today, I’m going to share with you some of the lessons I learned from one of the best books I’ve read: <a href="http://www.peachpit.com/store/seductive-interaction-design-creating-playful-fun-and-9780321725523">Seductive Interaction Design: Creating Playful, Fun, and Effective User Experiences</a>.
</p>
<p>I believe that you should read this book yourselves to fully appreciate all the information and insight the writer has to share. I will cover only the first half of the book. I will be pasting some excerpts, because there are many things that the writer describes a lot better than I could ever do. I will also be skipping a lot of the content for sake of brevity and of course because you should learn the rest from the book directly, not from me.</p>
<p>
So think of this article as a review, preview, or just a simple article written by someone who is so excited about a book she read that she just needs to share some of what she learned with you.
</p>
<p class="note">
<strong>Disclaimer:</strong> Images, blockquotes, and quotes are excerpted from Seductive Interaction Design: Creating Playful, Fun, and Effective User Experiences by Stephen P. Anderson. Copyright © 2011. Used with permission of Pearson Education, Inc. and New Riders.<br />
Link to book: <a href="http://www.peachpit.com/store/seductive-interaction-design-creating-playful-fun-and-9780321725523">Seductive Interaction Design: Creating Playful, Fun, and Effective User Experiences.</a><br />
</p>
<h3 class="deeplink" id="overview">OVERVIEW</h3>
<p>
Writer, designer, and speaker <a href="http://www.poetpainter.com/">Stephen Anderson</a> shows you in this book how the same tactics humans use to attract a mate can apply to the interactions between humans and interactive devices, to make people “fall in love” with your websites and/or applications.
</p>
<p>
The book focuses on human behavior, in both physical and digital contexts, and talks about what actually drives people and influences their behavior and “seduces” them into taking certain kinds of actions. It also studies a lot of examples of existing web applications, and explores the underlying psychological principles applied to the user experience of these applications, that make them as effective and successful as they are.
</p>
<p>One of the first examples mentioned in the book is LinkedIn and the effectiveness of the Profile Completeness process, specifically how LinkedIn manages to pull quite a bit of information out of millions of users through a series of prompts that are simple enough and yet very effective. By understanding what motivates people, they were able to get a lot of information out of them. And this is what the book is concerned with: why people do the things they do.</p>
<p>The concept of level completeness used in LinkedIn can also be found in games as a “progress dynamic”, with points and levels. It can also be found in other contexts, one of which is martial arts, where each “level” is represented by a colored belt, one you earn while advancing towards the black belt. <q>By having different colored belts [..] you get rewarded and recognized along the path to mastery. These belts are a tangible, achievable goal to work toward</q>.But why do we do the things we do when we have this kind of progress dynamic?</p>
<p>We could look at several ideas from psychology:</p>
<blockquote>
<ul style="list-style-type:none;">
<li><strong>Sequencing:</strong> We are more likely to take action when complex tasks are broken down into smaller tasks.</li>
<li><strong>Appropriate challenges:</strong> We delight in challenges, especially ones that strike a balance between being overwhelming and being boring.</li>
<li><strong>Status:</strong> We constantly assess how interactions enhance or diminish our standing relative to others and our personal best.</li>
<li><strong>Achievements:</strong> We are more likely to engage in activities in which meaningful achievements are recognized.</li>
</ul>
</blockquote>
<p>So always offer your users some kind of reward after every step they make throughout a sign up form, or any other kind of forms that require several steps along the way to completeness, like surveys, for example, and be clear about why you’re asking for the information you ask for, and help them understand that whatever information you ask from them will actually benefit <em>them</em>, even if you have other reasons you’re asking them for this information. <b>State your reasons in terms of how they would benefit <em> the user</em> to provide this information to you</b>, because <q>we’ve known that we are more interested in people who are interested in us. no one wants to sit and hear someone talk about themselves all night. The same is true in many online interactions</q>.</p>
<p>The writer then goes on to mention another example of great UX, which benefits both the user and the owners of the application, which is iTunes. He explains the process of signing up for iTunes in detail, and reveals the psychological part of the process, that makes users <em>want</em> to continue the process:</p>
<blockquote>
<ul style="list-style-type:none;">
<li><strong>Feedback loops: </strong> We’re engaged by situations in which we see our actions modify subsequent results.</li>
<li><strong>Curiosity:</strong> When teased with a small bit of interesting information, people want to know more.</li>
<li><strong>Visual imagery:</strong> Vision trumps all other senses and is the most direct way to perception.</li>
<li><strong>Recognition over recall:</strong> It’s easier to recognize things we have previously experienced than it is to recall them from memory.</li>
</ul>
</blockquote>
<p>This kind of perspective offers you a new way to look at user experiences. For example, one example of UX that is <em>different</em> and very attractive and more enjoyable, in my opinion, is the new form and survey experience that <a href="http://www.typeform.com/">Typeform</a> offers, because it uses several concepts including sequencing and visual imagery, and they use images for a lot of their questions and offer multiple choices which are easier to use than having to recall stuff from memory. All these elements make filling up forms and taking surveys easier and more enjoyable, or at the very least, a lot less boring.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/typeform1.png" />
<figcaption>Screenshot from the preview video on the Typeform website. The application offers users a set of images to choose from.</figcaption>
</figure>
<figure>
<img src="https://sarasoueidan.com/assets/images/typeform2.png" />
<figcaption>Screenshot from the preview video on the Typeform website. The application offers users an image with the question. "Vision trumps all other senses and is the most direct way to perception".</figcaption>
</figure>
<h3 class="deeplink" id="usability-psychology">Usability and Psychology</h3>
<p>A simple and straightforward differentiation between the roles of usability and psychology in user experience design is the following:</p>
<blockquote>
Usability clears the way for a good experience by eliminating troublesome interface distractions, but a great experience stems from something more—an awareness of why people could or do care. <b>The danger is in confusing “ease of use” with actually desiring to use something.</b> These are two entirely different things. Both are essential, but simply making something more usable won’t guarantee any more clicks or conversions. in this case, it was psychology that made this so engaging.
</blockquote>
<p>Here is an image showing the difference in roles between usability and psychology in user experience design:</p>
<figure class="floated">
<img src="https://sarasoueidan.com/assets/images/usability-psychology.png" alt="Usability vs Psychology" />
<figcaption>An image representing the role of each of usability and psychology in user experience design.</figcaption>
</figure>
<p>By using psychology to make your website/application move from being functional, reliable, usable, and convenient to being pleasurable and meaningful to the user. With this in mind, the writer then introduces a “user experience hierarchy of needs model” (shown in the image below)</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/hierarchy.png" alt="Hierarchy of Needs Model" />
<figcaption>User Experience Hierarchy of Needs model. From bottom to top is a basic product maturity continuum: a top to bottom focus starts with the experience you want people to have.</figcaption>
</figure>
<p>So if you want to create a revolutionary product, you have to think beyond basic functionality, usability, and convenience, and think about what kind of experience you want the user to have when using your product/website/application, but without forgetting the basics of usability.</p>
<h3 class="deeplink" id="aesthetics">Aesthetics, Beauty, and Behavior</h3>
<p>The first “Weapon” of seduction is Aesthetics. This section explores the relation between aesthetics and human cognition, affect, and how aesthetics help our brains make certain associations between a product, and other real-life objects, and how these associations will end up affecting how we feel about it, and consequently how we behave. </p>
<blockquote>
As user experience designers, we must consider every stimulus that might influence user interaction. […] “aesthetics examines our response to an object or phenomenon” (according to Wikipedia). In other words, aesthetics aren’t just about the artistic merit of Web buttons or other visual effects, but about how people respond to these elements. The question becomes: How do aesthetic design choices influence understanding and emotions, and how do understanding and emotions influence behavior?
[…] we’ll look at how aesthetics influences cognition, affect, and associations.
</blockquote>
<h4 class="deeplink" id="aesthetics-cognition">Aesthetics and Cognition</h4>
<p>
<q>Cognition is the process of knowing. Based on patterns and experiences, we learn how to understand the world around us [..] and aesthetics play a critical role in cognitive processing</q>, that is, in the way we perceive elements in the digital context.
</p>
<p>The human brain has its own way of interpreting color, shadows, shading, and other natural occurrences, and the role of aesthetics is to communicate the functionality of the elements we see to our brains. They provide the brain with cues that communicate how we should interact with these elements. So, when designing, designers should think about what each color means, in addition to the role and meaning of shadows and shading. </p>
<p>For example, think about a simple button (image below). The shadows, gradients, and beveled edges of the upper right button help the brain understand that this is in fact a button, and therefore it can be pressed, and that we can expect something to happen if we do. In this case, aesthetics communicate function. These shadows, gradients, and beveled edges <q>are perceived affordances—cues that communicate how a user can, and should, interact with an object. translation: if it looks like a button, it must be a button</q>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/buttons.png" alt="Buttons and Meaning of Gradients" style="margin-top:50px;" />
<img src="https://sarasoueidan.com/assets/images/alert.png" alt="Alert Message and Meaning of color" style="float:right" />
</figure>
<p style="clear:both;">The second image shows how wrong usage of colors can confuse the brain about the actual meaning and functionality of an element, in this case an alerted message.</p>
<p>The use of shadows also plays an important role in determining how elements look on a page, and which elements lay on top of others (stacking context), so special attention should be paid to these kinds of details. One of my favorite paragraphs in this book is this one in which the writer gives a golden tip:</p>
<blockquote>
Whatever the natural reference is— shadows, reflections, lighting, bevels—i like to ask designers, “Could you build a physical model of this page?” if you can’t, then the viewer will likely be disturbed, as something feels not quite right.
</blockquote>
<p>So, if you can build a physical model out of a digital design, then it can be interpreted correctly by the brain, otherwise it’s just not right!</p>
<p>In addition to cuing the brain to understand the functionality and shape of objects, aesthetics play in a role in determining the relationship between these objects.<q>For example, the law of proximity explains that if i place two or more items in a cluster together, you’ ll assume they are related</q>.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/proximity.png" alt="" />
</figure>
<p>Then there is also contrast and connectedness.<q>if one object has different characteristics from other objects, we perceive it as being different. This is known as contrast.” Additionally, elements connected by uniform visual properties are perceived as being more related than elements that are not connected. This is known as uniform connectedness</q>.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/connectedness.png" alt="" />
</figure>
<figure class="floated">
<img src="https://sarasoueidan.com/assets/images/mac-genie.png" alt="" />
</figure>
<p>Additionally, aesthetics help us understand the “space” in which we interact, for example, placing elements behind each other in a 3D space to communicate distance. Another example is the famous “genie effect” animation in Mac OS X, which communicates where a file is being stored/minimized for easy retrieval later.</p>
<h4 class="deeplink" id="aesthetics-emotion">Aesthetics and Emotion</h4>
<p>Some UX designers believe that by making a product easy to use and convenient, this will directly lead to creating a better, enjoyable user experience. But in this chapter, the writer proves that things that are enjoyable will be perceived as easy to use and convenient. Again to relate digital experiences to real-life interactions between people, <q>Think of how quickly we form judgments about people in the first few moments after we meet them. Conversely, think about how our personal appearance (our personal aesthetic) affects the way people perceive us; or how product packaging influences our perception of the product inside. We may know better, but we continue to judge a book by its cover</q>.</p>
<p>The way products and interfaces look says a lot about them. Take for example an application with a lot of attention to details. When you see this kind of attention, you subconsciously trust the application you’re interacting with more. On the other hand, imagine a UI with inconsistent fonts, odd paddings, line heights, and such details that can butcher even the greatest designs, “how might these sloppy UI details affect our perception of the application?” How can you trust an application whose owner wasn’t attentive enough to care about these small details of their own product? How will they be able to pay attention to our needs if they can’t even pay attention to the small details in their product?</p>
<p>There are also a lot of studies mentioned in the book that prove that <q>not only do aesthetics affect perceived usability, they also influence actual performance</q>. I’m going to mention only one short experiment for the sake of brevity:</p>
<blockquote>
One study, “The Influence of Design aesthetics in usability testing: effects on user Performance and Perceived usability,” (Sonderegger and Sauer, 2009), presented adolescents with one of two mobile phones, an attractive one, and one less so. The conclusion? “The visual appearance of the phone had a positive effect on performance, leading to reduced task completion times for the attractive model.
</blockquote>
<p>All the experiments the writer mentions lead us to one firm conclusion:</p>
<blockquote>
The more we learn about people, and how our brains process information, the more we see the truth of that phrase: form and function aren’t separate. If form exists independently of function, and we can treat aesthetics and function as two separate elements, then we ignore the evidence that beauty is much more than decoration. Our brains can’t help but agree.
</blockquote>
<h4 class="deeplink" id="aesthetics-associations">Aesthetics and Associations</h4>
<p>Aesthetics play a major role in associations our brains make between objects. Our brains tend to try to connect objects to other objects, and when that is done it shares the characteristics of one of these objects with the other.</p>
<p>The best way to explain this point is to just paste one of the (interesting) examples the writer mentions in the book: Apple products!</p>
<blockquote>
<p>In a 2005 essay on design and perceptions, Luke Williams recounts how another designer discovered why so many people think of the iPod as a “clean” device. Apparently, this designer had been sitting on the toilet (where all great ideas happen!) when it occurred to him that the iPod references the same materials used in a bathroom, “the shiny white porcelain of the bathtub and the reflective chrome of the faucet on the wash basin.” This might sound laughable, until you factor in that Jonathan ives, apple’s senior vice president of design, once worked for an agency that designed—you guessed it— bathroom appliances. Coincidence? Perhaps. What’s important is that “consciously or unconsciously, the iPod materials reference a convention of ‘cleanliness’ that everybody interacts with every day—a bathroom.
</p>
<p>We’re talking about human perception, and the system of conventions that shapes our perceptions. Perception is essential to the process of design.
These aesthetic associations are evident in other apple products. If you own an apple laptop, you may have noticed the soothing sleep-light indicator that’s visible when your computer is “sleeping.” The rate at which this light fades in and out is comparable to that of the average respiratory rate for adults, about 12 to 20 breaths per minute. Coincidence? apple owns the patent for a Breathing status Led indicator (Us 6,658,577 B2), which “mimics the rhythm of breathing which is psychologically appealing".</p>
<img src="https://sarasoueidan.com/assets/images/ipod.png" alt="" width="250" height="230" />
<p>One final example: when apple launched the original iPod shuffle, they compared it directly to a pack of gum, due to the equivalent sizes of the two products. This is a great example of a conceptual metaphor, in which we make sense of new information by associating it with something we’re already familiar with. </p>
</blockquote>
<h4 class="deeplink" id="power-of-faces">The Power of Faces</h4>
<p>Including faces in our online interactions also affects the associations our brains make, because faces carry with them some kind of associations, and can help build trust because of a higher fidelity of information that the application or website presents.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/faces.png" alt="" />
</figure>
<p>One example to best explain this notion is Facebook's logout screen. </p>
<blockquote>
The original Facebook deactivation page was pretty boring. It simply stated: “We’re sorry you’re leaving. tell us why Facebook was not useful.” One of the designers suggested that closing your account be “more like leaving summer camp (you know, a place that has all your friends and you don’t want to leave.)”
<img src="https://sarasoueidan.com/assets/images/facebook.png" alt="" />
Inspired by this concept, the design team created a new deactivation page that pulls faces from a few of your friends’ profiles, along with the message that asks, “are you sure you want to deactivate your account? Your 498 friends will no longer be able to keep in touch with you.” has this made a difference? according to Julie Zhuo, design manager at Facebook, this has reduced the deactivation rate by 7 percent. at least a million fewer users have deactivated their accounts!
</blockquote>
<h3 class="deeplink" id="playful-seduction-techniques">Playful Seduction Techniques</h3>
<blockquote>
In dating terms, it’s easy to think, “People will like me for who I am.” The truth is people have to be interested just enough to get to know you (your app) in the first place. What we’re talking about in this chapter are ways to design interactions that are more interesting and playful—interactions that engage people in both intellectually and emotionally. This leads to experiences that do more than merely work, they delight people.
</blockquote>
<h4 class="deeplink" id="fun">Be Fun</h4>
<p>We all like funny people. And by funny I don’t mean people who are always making jokes and trying to make you laugh, because those can become really annoying! By funny people I mean people who are fun to be around, who always have their way to make you smile, and who are great talkers, and can get a message to you in a fun way that sticks into your brains and that you’re more likely to remember later, and smile about it! </p>
<p>By including humor in digital contexts, you engage people in a meaningful and memorable way. One of the examples that show the kind of fun you can use is the Southwest Airlines company.
</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/southwest.png" alt="Southwest" />
<figcaption>
Some messages that the Southwest Airlines company uses that include fun phrases.
</figcaption>
</figure>
<p>Some may argue that humor is not always appropriate, so the writer has another golden advice to give:</p>
<blockquote>
<p>If it’s appropriate in a real-world interaction, why not online as well? Are we suddenly transformed into emotionless automatons when we sit in front of a screen? No.</p>
[...]
<p>Humor is appropriate (or inappropriate) based on the situation, not the industry.”</p>
</blockquote>
<figure>
<img src="https://sarasoueidan.com/assets/images/popup.png" alt="Fun Popup" />
<figcaption>
An alert message that uses humor to convey a message.
</figcaption>
</figure>
<h4 class="deeplink" id="unpredictable">Be Unpredictable</h4>
<blockquote>
Our brains are aroused by the unexpected. While stability and a sense of control are no doubt critical user interface principles, there’s something exciting about the unexpected. not knowing what to expect heightens our anxiety, and our curiosity. Our brains are aroused by new and unexpected discoveries within our normal routines.
</blockquote>
<p>One of the simplest way to make an experience more enjoyable after repeated visits, is by breaking the routine. One of the simplest and most obvious examples of offering a slight surprise and breaking the routine is the Google Search homepage. The Google logo changes depending on the occasion. This little change adds a new flavor to our daily visits, that would otherwise be all boring and the same, especially that the Google homepage design is already too simple to start with. So add these little design touches help break the routine and keep our brains expecting something new at different occasions.</p>
<p>Another example of breaking routines is for example changing the content of a confirmation message every time it pops up so that it’s different every time, or changing an image on the homepage after repeated visits, or delightful messages that pop up in unexpected places. These messages don’t have to be necessary, yet they could be pleasant enough to stick in the head of your users or readers.</p>
<p>One perfect example of a delightful surprise the writer mentions is a note he saw while he was going up the stairs in a hotel, where he was surprised by a “Everything is going to be alright” written in uppercase on one of the stairs (image below), that made him grin, and was so pleasant that he still remembers it and thought it was worthy of mentioning as a great example in his book.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/stairs.png" alt="Stairs" />
</figure>
<blockquote>
A good date is full of delightful moments— some planned, some not—that make the overall experience memorable and pleasant. What does my wife remember from our first date? among other fond memories, it rained and we got our shoes stuck in the mud!
</blockquote>
<p>These kinds of small delightful surprises make the interaction with the digital world seem more human, which is a very important aspect if you’re trying to sell yourself as a trustworthy person.</p>
<p>So what makes a good present or surprise? Another golden advice from the writer:</p>
<blockquote>
A good gift is one that pumps up the recipient.
</blockquote>
<p>Think of the word pump: P is for pleasurable, U is for unexpected, M is for meaningful (useful, not generic), and the last P is for pleasantly packaged.</p>
<h4 class="deeplink" id="mysterious">Be Mysterious</h4>
<p>
<q>In new relationships, flirtation often involves some element of playful teasing</q>, and a similar kind of teasing can be applied to the relationship between users and a website or application.
</p>
<p>Curiosity is a powerful human drive that pushes us to do a lot of the things we do. It’s probably the reason why I read the book in the first place. I was curious to know how a website or application can be “Seductive”, I was even more curious after I started reading to understand how people think, and understand how my brain works, and why <em>I</em> do the things I do.</p>
<p>Part of the role of usability is make things clear to the user, and remove all roadblocks and ambiguous elements that make a user experience become a rather frustrating one.</p>
<p>Just like we have to care about the basics and make an application or product usable and functional, and after that we can cross that line to make it pleasurable and memorable,<b> we can also start thinking about adding some kind of <em>controlled</em> uncertainty to the experience after having provided the user with the clarity he needs, thus introducing a level of thrill and suspense to the experience</b>.</p>
<p>Sometimes giving the user all the information they want from the first visit rids them of their interest and leaves no more room for curiosity to drive them forward to further explore the application at hand.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/curiosity1.png" alt="Curiosity Zone" />
</figure>
<p>You want to make sure that you <em>tease</em> them with as little information as necessary, enough to drive them into the “curiosity zone”.</p>
<figure>
<img src="https://sarasoueidan.com/assets/images/curiosity2.png" alt="Curiosity Zone" />
</figure>
<blockquote>
<p>Information can be presented in a manner that is straightforward or curious. If we opt for the latter, we are guaranteed not only attention, but probably higher engagement as well— curiosity demands that we know more!</p>
<p>[…]</p>
<p></p><p>When we become aware that information is missing—when something changes from being known (or so we thought) to an unknown state—we become curious. This is the explanation of curiosity posed by behavioral economist George Loewenstein in his information gap theory. Loewenstein says, “curiosity happens when we feel a gap in our knowledge.”</p><p></p>
<p>[…]</p>
<p></p><p>The feeling we get from these information gaps is best described as deprivation, which is critical to understanding why we are motivated by curiosity. to “eliminate the feeling of deprivation,” we seek out the missing information.</p><p></p>
<p>[…]</p>
<p></p><p>Simply stated: I’m curious because there’s a gap between “what I know and what I want to know.</p><p></p>
</blockquote>
<p>It’s human to be curious. And it’s part of our nature to seek to solve mysteries around us. The kind of curiosity the writer refers to in his book is akin to teasing. Teasing people by making them aware that there is something they <em>don’t</em> know. But there are a few ways to make this teasing effective, and I believe the most important one is to let them know that the information you’re hiding away from them will benefit <em>them</em> the most. Also, make sure that once they get to the point where they uncover the information you withheld from them at the beginning, they find that this information meets their expectations, the expectations they built because of <em>you</em>. Also, <q>don’t lure users with something that is given away freely elsewhere</q>. </p>
<h4 class="deeplink" id="self-expression">Let others express themselves around you</h4>
<p>The final principle in the playful seduction section is <em>self-expression</em>.</p>
<p>Giving the users the ability to express themselves in your application, by giving them, for example, the ability to customize the application and make it more personal, is very important when the users expect this kind of option to be available to them, and even when they don’t!</p>
<blockquote>
The need for self-expression shows up in just about any area where people are allowed to control something, especially where this control is tied to an identity.
</blockquote>
<p>Some examples of allowing users to express and apply their personal identity to an application would be to allow them to change the theme, or use custom emoticons, or change the content and add/remove what they want, change the layout and theme, and so on.</p>
<p>If you’re one of the people who use Windows’ MSN chat application, then you definitely know that for the past couple of months or more, the company was preparing MSN users to switch, or “upgrade”, to Skype. I am one of the individuals who sincerely hated this “upgrade” for one simple reason: Skype doesn’t allow you to use custom emoticons. And after reading a lot of comments on the Skype blog, I realized that I am only one of so many people who hated this “upgrade” as well, and my intention was very clear: to <em> not</em> make the switch. Why? Because my MSN is customized to my needs, and Skype doesn’t allow you to add custom emoticons. I love custom emoticons. I can’t chat without them. And I really believe this should not be called an upgrade because IMO to upgrade means to make better, and when your newer version lacks a lot of the best features of your previous one, then this, IMO, is more of a downgrade. Microsoft should’ve given this move a lot of thought before actually deciding to move forward with it.</p>
<p>The point is, giving your users the ability to customize your application or express themselves in any way is a great way to make the application feel more personal and thus enjoyable for them. And if you ever decide to do that, please don’t take that option away from them later. If you add it to your app, just make it stick. </p>
<h5 class="deeplink" id="final-words">Final Words</h5>
<p>I only covered the first two sections of the book in this article. The amount of information, tips, and insight I wrote here is just a drop from the sea of what you can find in the book. The next two sections are titled “The Subtle Art of Seduction” and “The Game of Seduction”, with a <em>lot</em> more insight and tips in them.</p>
<p>If you’re a UX designer, a designer, a web developer, or any person interested in learning more about human behavior, or interested in making your products more popular or trying to build a stronger online existence, then this is definitely a must-read for you. I cannot recommend this book enough.</p>
<p>After reading this book, I’m definitely never going to look at applications and UX like I used to, because it gave me a lot of insight, and helped me see and understand the reasons behind successful user experiences, and taught me how I could apply these principles even to a small website, not just a huge application.</p>
<p>I hope you enjoyed this article and found it useful. Thank you for reading! </p>
How to Create Windows-8-like animations with CSS3 and jQuery
2013-02-11T00:00:00Z
https://sarasoueidan.com/blog/windows8-animations/
<p class="deck">I have recently realized that CSS3 3D transforms have been out there for quite a long time now and yet I haven't experimented with them yet. I have also been using Windows 8 for a while now, and the first thing that struck me as impressive about it was the transitions and animations built into the dashboard, so I thought it would be really cool if my first experiment with CSS 3D transforms would be to recreate those animations and effects. So, here goes the tutorial on how I did that.
</p>
<p class="note warning">Please note that this demo works only in <a href="http://caniuse.com/#feat=transforms3d">browsers that support</a> the CSS3 properties used. </p>
<p class="note">For the sake of brevity in the example code, I am using the un-prefixed CSS properties, but you will find the prefixes in the downloadeable source code on Github.</p>
<h3 class="deeplink" id="markup">The Markup</h3>
<p>The demo's structure is pretty simple: The dashboard is a list of tiles, of three sizes, small, big, and 2xbig, floated inside 3 columns. And then there are their corresponding "pages". A page is an overlay which opens when u click on one of the boxes on the dashboard. It represents an app on desktop, with each of the tiles being the shortcut to that app.</p>
<p>Each tile will open up a corresponding page. There are two kinds of page transitions included in the Windows 8 dashboard: one that opens the page in a 3D rotating effect from the right of the screen, and one that slides the page in and back to the left. We will define a class name for each type of page as <code> s-page</code> for the pages that slide from and to the left, and <code> r-page</code> for pages that rotate open from the right.</p>
<p>Now, for each tile, we need to specify what type of page it opens (depending on the effect you want for that page). We will define the type of the page for each tile in a custom data attribute called <code> data-page-type</code>, this will take care of applying the right class names triggering the right animations later on.</p>
<p>Each page should also have a name. The page name for a certain app will be different from that of another app, so the "Skype" tile opens up a page called "skype-app" for example. I've used only two page names in this example, which are repeated for all tiles, and used a <code> custom-page</code> name for the last tile for sake of example. You'll probably have to add a different page for each tile, hence a different page name specified in every tile.</p>
<p>Here's the markup for the whole dashboard (tiles and pages):</p>
<pre class="brush:html;" style="max-height:600px; overflow-y: scroll;">
<div class="demo-wrapper">
<!-- classnames for the pages should include: 1) type of page 2) page name-->
<div class="s-page random-restored-page">
<div class="page-content">
<h2 class="page-title">Some minimized App</h3>
<div class="close-button s-close-button">x</div>
</div>
</div>
<div class="s-page custom-page">
<div class="page-content">
<h2 class="page-title">Thank You!</h3>
<div class="close-button s-close-button">x</div>
</div>
</div>
<div class="r-page random-r-page">
<div class="page-content">
<h2 class="page-title">App Screen</h3>
<p>Chew iPad power cord chew iPad power cord attack feet chase mice leave dead animals as gifts and stick butt in face chew iPad power cord. Chase mice. Run in circles use lap as chair why must they do that. Intrigued by the shower destroy couch leave hair everywhere sleep on keyboard chew iPad power cord. Use lap as chair. Missing until dinner time stand in front of the computer screen, intently sniff hand. Find something else more interesting. Destroy couch play time so inspect anything brought into the house hate dog burrow under covers. Sleep on keyboard destroy couch so hate dog so hide when guests come over. Chase mice destroy couch lick butt throwup on your pillow use lap as chair yet intrigued by the shower but climb leg. Stare at ceiling make muffins or hunt anything that moves claw drapes. Intently sniff hand intrigued by the shower. Why must they do that. Cat snacks leave dead animals as gifts or inspect anything brought into the house sweet beast so stare at ceiling give attitude. Flop over claw drapes but sun bathe lick butt, and chase mice. Rub face on everything lick butt leave hair everywhere lick butt, missing until dinner time for use lap as chair lick butt. Make muffins leave dead animals as gifts play time. Chew foot intrigued by the shower stare at ceiling inspect anything brought into the house yet hopped up on goofballs.
Hunt anything that moves intently sniff hand for hunt anything that moves play time. Chew foot climb leg throwup on your pillow so lick butt yet make muffins hate dog. Intrigued by the shower. Intently sniff hand shake treat bag. Cat snacks burrow under covers make muffins but all of a sudden go crazy find something else more interesting. Flop over chase mice. Give attitude. Inspect anything brought into the house. Stick butt in face sun bathe so find something else more interesting and intrigued by the shower. Rub face on everything use lap as chair.
Under the bed claw drapes chase mice but leave hair everywhere yet make muffins yet claw drapes. Use lap as chair. Find something else more interesting stretch for under the bed. Nap all day intrigued by the shower, hate dog sweet beast intently sniff hand so hate dog nap all day. Swat at dog hide when guests come over and mark territory chase mice for cat snacks. Use lap as chair. Lick butt throwup on your pillow need to chase tail.
Mark territory. Stick butt in face shake treat bag yet hunt anything that moves, yet hopped up on goofballs yet stare at ceiling under the bed. Give attitude chase imaginary bugs stretch so hunt anything that moves so hide when guests come over but intrigued by the shower find something else more interesting. Make muffins behind the couch for chew foot. Sweet beast flop over but throwup on your pillow. Intently sniff hand use lap as chair and missing until dinner time and chase imaginary bugs.
</p>
</div>
<div class="close-button r-close-button">x</div>
</div>
<!--each tile should specify what page type it opens (to determine which animation) and the corresponding page name it should open-->
<div class="dashboard clearfix">
<ul class="tiles">
<div class="col1 clearfix">
<li class="tile tile-big tile-1 slideTextUp" data-page-type="r-page" data-page-name="random-r-page">
<div><p>This tile's content slides up</p></div>
<div><p>View all tasks</p></div>
</li>
<li class="tile tile-small tile tile-2 slideTextRight" data-page-type="s-page" data-page-name ="random-restored-page">
<div><p class="icon-arrow-right"></p></div>
<div><p>Tile's content slides right. Page opens from left</p></div>
</li>
<li class="tile tile-small last tile-3" data-page-type="r-page" data-page-name="random-r-page">
<p class="icon-calendar-alt-fill"></p>
</li>
<li class="tile tile-big tile-4" data-page-type="r-page" data-page-name="random-r-page">
<figure>
<img src="images/blue.jpg" />
<figcaption class="tile-caption caption-left">Slide-out Caption from left</figcaption>
</figure>
</li>
</div>
<div class="col2 clearfix">
<li class="tile tile-big tile-5" data-page-type="r-page" data-page-name="random-r-page">
<div><p><span class="icon-cloudy"></span>Weather</p></div>
</li>
<li class="tile tile-big tile-6 slideTextLeft" data-page-type="r-page" data-page-name="random-r-page">
<div><p><span class="icon-skype"></span>Skype</p></div>
<div><p>Make a Call</p></div>
</li>
<!--Tiles with a 3D effect should have the following structure:
1) a container inside the tile with class of .faces
2) 2 figure elements, one with class .front and the other with class .back-->
<li class="tile tile-small tile-7 rotate3d rotate3dX" data-page-type="r-page" data-page-name="random-r-page">
<div class="faces">
<div class="front"><span class="icon-picassa"></span></div>
<div class="back"><p>Launch Picassa</p></div>
</div>
</li>
<li class="tile tile-small last tile-8 rotate3d rotate3dY" data-page-type="r-page" data-page-name="random-r-page">
<div class="faces">
<div class="front"><span class="icon-instagram"></span></div>
<div class="back"><p>Launch Instagram</p></div>
</div>
</li>
</div>
<div class="col3 clearfix">
<li class="tile tile-2xbig tile-9" data-page-type="custom-page" data-page-name="random-r-page">
<figure>
<img src="images/summer.jpg" />
<figcaption class="tile-caption caption-bottom">Fixed Caption: Some Subtitle or Tile Description Goes Here with some kinda link or anything
</figure>
</li>
<li class="tile tile-big tile-10" data-page-type="s-page" data-page-name="custom-page">
<div><p>Windows-8-like Animations with CSS3 & jQuery © Sara Soueidan. Licensed under MIT.</p></div>
</li>
</div>
</ul>
</div><!--end dashboard-->
</div>
```
<p>The icon font I'm using is from <strong>Icomoon</strong>.</p>
<p>What will happen is that the Javascript will get the name and type of page to be opened when a tile is clicked, and then, according to the type of the page, it will apply specific class names to the page (whose name we have also retrieved from the <code>data-page-name</code> attribute) to open it with the specified type of animation for each class applied.</p>
<h3 class="deeplink" id="css">The CSS</h3>
<p class="note">Please note that I'm taking a mobile-first approach to the styles, which we'll then make responsive in the media queries section.</p>
<p>First, the styles for the demo wrapper, the container in which the whole demo will be contained. We'll define general styles, and make sure to set a <code>perspective</code> to activate the 3D space, otherwise, the whole demo will look flat and two dimensional.</p>
```css
.demo-wrapper {
padding: 2em .5em;
width: 100%;
height:100%;
perspective: 3300px;
position: relative;
}
```
<p>Now let's start with the dashboard styles and animations.</p>
<p>The first animation applied to the dashboard is fired when the page loads. The dashboard is initially hidden and translated to the right of the screen, and fades and translates in to position on page load.</p>
```css
.dashboard {
margin: 0 auto;
width: 100%;
padding: 1em;
transform: translateX(200px);
opacity:0;
animation: start 1s ease-out forwards;
}
@keyframes start{
0%{
transform: translateX(200px);
opacity:0;
}
50%{
opacity:1;
}
100%{
transform: translateX(0);
opacity:1;
}
}
```
<p>The dashboard also fades into the view and fades back when a tile is clicked. Once a tile is clicked, the dashboard translates back along the z-axis, decreases in size, and fades its opacity gradually till it becomes 0. And when an opened page is closed, the dashboard fades back into the view.</p>
<p>The three columns in the dashboard fade in one after the other, with a slight delay between them. When a page is closed, a class name is added to each column (via Javascript), and each of these classes calls the animation with a certain delay.</p>
<p>Here are the classes and the animations applied to the dashboard upon clicking the tiles and closing the pages:</p>
```css
.fadeOutback{
animation: fadeOutBack 0.3s ease-out 1 normal forwards;
}
.fadeInForward-1, .fadeInForward-2, .fadeInForward-3 {
/*remember: in the second animation u have to set the final values reached by the first one*/
opacity:0;
transform: translateZ(-5em) scale(0.75);
animation: fadeInForward .5s cubic-bezier(.03,.93,.43,.77) .4s normal forwards;
}
.fadeInForward-2{
animation-delay: .55s;
}
.fadeInForward-3{
animation-delay: .7s;
}
@keyframes fadeOutBack{
0% {transform: translateX(-2em) scale(1); opacity:1;}
70% {transform: translateZ(-5em) scale(0.6); opacity:0.5;}
95% {transform: translateZ(-5em) scale(0.6); opacity:0.5;}
100% {transform: translateZ(-5em) scale(0); opacity:0;}
}
@keyframes fadeInForward{
0% {transform: translateZ(-5em) scale(0); opacity:0;}
100% {transform: translateZ(0) scale(1); opacity:1;}
}
```
<p>Now we're going to style the pages.</p>
```css
.r-page {
width: 100%;
height: 100%;
text-align: center;
font-size: 2em;
font-weight: 300;
position: absolute;
right: 0;
top: 0;
left:0;
bottom:0;
opacity: 0;
color: white;
z-index: 10;
padding:10px;
transform-origin: 100% 0%;
transform: rotateY(-90deg) translateZ(5em)
}
.s-page {
color: white;
z-index: 10;
text-align: center;
font-size: 2em;
font-weight: 300;
}
.page-content{
overflow-y:auto;
max-height:100%;
font-size:.6em;
padding:.6em;
text-align:left;
}
.s-page, .r-page{
background-color: white;
color:black;
}
.page-title {
margin: .25em 0;
font-weight: 100;
font-size: 3em;
text-align:center;
}
.close-button {
font-size: 1.5em;
width: 1em;
height: 1em;
position: absolute;
top: .75em;
right: .75em;
cursor: pointer;
line-height: .8em;
text-align: center
}
```
<p>I've set the original position of each <code>r-page</code> in the 3D space by first rotating it about the y-axis (the vertical axis), then I moved the page 5em to the left of the screen by using <code>translateZ</code>. Always remember: when u transform an element in 3D, you transform its coordinate system along with it. What I wanted to do is move the page 5em to the left of the screen, but instead of using <code>translateX</code> I used <code>translateZ</code>, because after the first tranformation (rotation about y axis), the coordinate system was also rotated, so now the z-axis points towards the left, and the x-axis is pointing towards you, the viewer.</p>
<p>All the pages, except the s-page app page, have the same initial position in the 3D space. The <code>s-page</code>s, on the other hand, are positioned -150% left of the screen off canvas, so that they slide into view when their animation is fired.</p>
<p>
Once the tile for each page is clicked, a corresponding class is added (via javascript) to the page that will open, and each class calls for a certain animation. So, each page will get a class name that will define the 3D effect for the page.
</p>
<p>These are the class names that trigger the opening and closing of the pages, along with the animations defined for each class.</p>
```css
/* opens the r-page type pages*/
.openpage{
animation: rotatePageInFromRight 1s cubic-bezier(.66,.04,.36,1.03) 1 normal forwards;
}
/* closes the r-page type pages */
.slidePageLeft{
transform: rotateY(0) translateZ(0) ; opacity:1;
animation:slidePageLeft .8s ease-out 1 normal forwards;
}
/* opens the s-page type pages */
.slidePageInFromLeft{
animation: slidePageInFromLeft .8s cubic-bezier(.01,1,.22,.99) 1 0.25s normal forwards;
}
/* closes the s-page type pages*/
.slidePageBackLeft{
opacity:1;
left:0;
animation: slidePageBackLeft .8s ease-out 1 normal forwards;
}
```
<p>I'm using the <code>animation</code> <a href="http://www.w3.org/TR/css3-animations/#animation-shorthand-property">shorthand property</a> here. The last value <code>forwards</code> corresponds to the <code>animation-fill-mode</code> property, which must be set to <code>forwards</code>, otherwise the page will get back to its initial "closed" position after the animation is over. So, in order to keep the page open, and be able to create sequential animations, the element has to stay in the final state defined by the first animation, and from there start the second animation.</p>
<p>These are the animations for the classes applied to the pages:</p>
```css
@keyframes rotatePageInFromRight{
0% {transform:rotateY(-90deg) translateZ(5em);opacity:0}
30% {opacity:1}
100% {transform: rotateY(0deg) translateZ(0) ; opacity:1}
}
/*When the close-button is clicked, the page slides to the left*/
/*note that the start of the second animation is the same state as the
end of the previous one*/
@keyframes slidePageLeft{
0% {left:0; transform: rotateY(0deg) translateZ(0) ; opacity:1}
70% {opacity:1;}
100% {opacity:0; left:-150%; transform: rotateY(0deg)}
}
@keyframes slidePageInFromLeft{
0% {opacity:0; }
30% {opacity:1}
100% {opacity:1; left:0;}
}
@keyframes slidePageBackLeft{
0% {opacity:1; left:0; transform: scale(0.95);}
10% {transform: scale(0.9);}
70% {opacity:1;}
100% {opacity:0; left:-150%;}
}
```
<p>Last but not least we'll style the dashboard tiles and define the transitions and animations applied to them when they are hovered.</p>
<p>General styles defining the size of the tiles:</p>
```css
.tile{
float: left;
margin: 0 auto 1%;
color: white;
font-size: 1.3em;
text-align: center;
height: 8em;
font-weight: 300;
overflow: hidden;
cursor: pointer;
position:relative;
background-color: #fff;
color: #333;
position:relative;
transition: background-color 0.2s ease-out
}
.tile-2xbig{
height:16.15em;
width:100%;
}
.tile-big {
width: 100%
}
.tile-small {
width: 49%;
margin-right: 2%
}
.tile-small.last {
margin-right: 0
}
```
<p>A couple of tiles contain an image along with an image caption. These tiles will get a class <code>fig-tile</code> to determine their type in the Javascript code. The colors used for the text and background of the corresponding page will be retrieved from the colors of the caption, so don't forget to define them.The caption can be either fixed, or it can slide in when the tile is hovered:</p>
```css
.tile-caption{
position:absolute;
z-index:1;
background-color: #455962;
color:#fff;
font-size:1em;
padding:1em;
text-align: left;
}
.caption-bottom{
left:0;
bottom:0;
right:0;
height:40%;
}
.caption-left{
left:-100%;
top:0;
bottom:0;
width:40%;
transition: left .3s linear;
}
.tile:hover .caption-left{
left:0;
}
```
<p>Regular tiles, with no special kind of animation, will change their background and text color on hover. In order to make sure the text is vertically centered in each tile, each one will contain a <code>div</code> with a paragraph containing the text. We'll use the table-cell display property to center this text vertically.</p>
```css
.tile div{
position:absolute;
top:0; left:0; right:0; bottom:0;
width:100%;
height:100%;
text-align:center;
display:table;
padding:0 1em;
transition: all .3s ease;
}
.tile div p{
display:table-cell;
vertical-align:middle;
}
```
<p>I'll skip the general styles of the tiles for sake of brevity, but make sure you set a background and text color on all tiles, even the ones that will be covered by an image, because these colors will be retrieved via Javascript and set as the colors for the corresponding page of this tile. Let's move on to the animations and transitions on the tiles.</p>
<p>Tiles with text sliding inside of them will contain two <code>div</code>s, each div will be like a "face" or a block inside the tile. These <code>div</code>s are positioned absolutely, and moved on hover according to the direction of slide we want.</p>
<p>For a tile's text to slide up on hover, we'll apply a class <code>slideTextUp</code>.</p>
```css
/* slide text inside tile up */
/* 2nd div will be positioned below the bottom of the tile*/
.slideTextUp div:nth-child(2){
top:100%;
}
/*both divs will be translated up on hover*/
.slideTextUp:hover div{
transform: translateY(-100%);
}
.tile-1 p{
font-size:1.3em;
}
```
<p>Similarly, tiles with text sliding to the right and left, will get class names <code>slideTextLeft</code> and <code>slideTextRight</code>, with a similar structure as the above tile.</p>
```css
/* slide text inside tile to the right*/
.slideTextRight div:first-child{
left:-100%;
}
.slideTextRight:hover div{
transform: translateX(100%);
}
/* slide text inside tile to the left */
.slideTextLeft div:nth-child(2){
left:100%;
}
.slideTextLeft:hover div{
transform: translateX(-100%);
}
```
<p>A couple tiles have a different hover effect, they rotate to reveal the back face of the tile. This effect is a very simple and basic "card flip" effect. I won't get into the details of this effect, but if you're new to this, you can read more about it in <a href="http://desandro.github.io/3dtransforms/docs/card-flip.html">this excellent tutorial</a> by David De Sandro.</p>
<p>For this flipping effect, apply a <code>rotate3d</code> class to the tile you want to flip. For a card with a vertical flip we'll add a class <code>rotate3dY</code>, and for a horizontal flip we'll apply a class <code>rotate3dX</code> (in addition to the <code>rotate3d</code> class), and we'll apply the following styles: </p>
```css
/* rotate tile in 3D*/
.rotate3d{
/* add a perspective to the tile to activate 3d space inside it*/
perspective: 800px;
overflow: visible;
}
.faces{
/* preserve the 3d space in the container wrapping the two "faces" and define a transition */
transform-style: preserve-3d;
transition: transform 1s;
}
.faces div {
/* position faces on top of each other */
display: block;
position: absolute;
top:0; left:0; right:0; bottom:0;
width: 100%;
height: 100%;
/* hide backface visibility so that the back of the face is hidden when it's rotated */
backface-visibility: hidden;
}
```
<p>We'll rotate one of the faces in the tiles so that the two faces are back to back.</p>
```css
.rotate3dY .back{
transform: rotateY( 180deg );
}
.rotate3dX .back{
transform: rotateX( 180deg );
}
```
<p>And when the tile is hovered, the <code>.faces</code> <code>div</code> will be rotated to reveal the back face. </p>
```css
.rotate3dY:hover .faces:hover{
transform: rotateY( 180deg );
}
.rotate3dX:hover .faces:hover{
transform: rotateX( 180deg );
}
```
<p>For styles rotating in 3D, remember to set a background and text color for the <code>.front</code> face, so that these colors be retrieved and set to the tile's page when it is opened.</p><p>
</p><p>And that's all for the styles and animations!</p>
<p>Now let's define responsive styles for the dashboard. Dashboard columns are initially full-width on small screens (remember we're starting mobile-first), and they will be floated next to each other on big screens.</p>
```css
.col1,
.col2,
.col3 {
width: 99%;
margin: 1em auto
}
@media screen and (min-width: 43.75em) {
.col1,
.col2,
.col3 {
float: left;
margin-right: 1%;
width: 49%
}
.page-title{
font-size:2.5em;
}
.page-content{
font-size:1em;
}
.close-button{
font-size:2em;
}
}
@media screen and (min-width: 64em) {
.col1,
.col2,
.col3 {
float: left;
margin-right: .5%;
width: 31%
}
.col3 {
margin-right: 0
}
.col1 {
margin-left: 2em
}
.page-title{
font-size:3.5em;
}
}
```
<h3 class="deeplink" id="javascript">The Javascript</h3>
<p>All click events will be handled wtih Javascript. I'll be using jQuery for this example. Event handlers are going to be set on each of the dashboard tiles, and when a click event is detected, we're going to retrieve the name and type of the corresponding page from the <code>data-page-type</code> and <code>data-page-name</code> attributes, and use these to open the page.</p>
<p>Other click events will be handled when clicking on the close button in each page. The close button for each page type will apply the suitable class names to close this page type.</p>
<p>Additionally, in order to give each page the same background color and text color as its corresponding tile, we will first loop through the tiles, retrieve its colors, and then applies it to its corresponsing page. If a tile has a <code>rotate3d</code> class, it looks for the background-color of the front "face" of the tile, and applies that to the page.</p>
<pre class="brush:javascript;">
(function(){
//get the background-color for each tile and apply it as background color for the cooresponding screen
$('.tile').each(function(){
var $this= $(this),
page = $this.data('page-name'),
bgcolor = $this.css('background-color'),
textColor = $this.css('color');
//if the tile rotates, we'll use the colors of the front face
if($this.hasClass('rotate3d')) {
frontface = $this.find('.front');
bgcolor = frontface.css('background-color');
textColor = frontface.css('color');
}
//if the tile has an image and a caption, we'll use the caption styles
if($this.hasClass('fig-tile')) {
caption = $this.find('figcaption');
bgcolor = caption.css('background-color');
textColor = caption.css('color');
}
$this.on('click',function(){
$('.'+page).css({'background-color': bgcolor, 'color': textColor})
.find('.close-button').css({'background-color': textColor, 'color': bgcolor});
});
});
function showDashBoard(){
for(var i = 1; i <= 3; i++) {
$('.col'+i).each(function(){
$(this).addClass('fadeInForward-'+i).removeClass('fadeOutback');
});
}
}
function fadeDashBoard(){
for(var i = 1; i <= 3; i++) {
$('.col'+i).addClass('fadeOutback').removeClass('fadeInForward-'+i);
}
}
//listen for when a tile is clicked
//retrieve the type of page it opens from its data attribute
//based on the type of page, add corresponding class to page and fade the dashboard
$('.tile').each(function(){
var $this= $(this),
pageType = $this.data('page-type'),
page = $this.data('page-name');
$this.on('click',function(){
if(pageType === "s-page"){
fadeDashBoard();
$('.'+page).addClass('slidePageInFromLeft').removeClass('slidePageBackLeft');
}
else{
$('.'+page).addClass('openpage');
fadeDashBoard();
}
});
});
//when a close button is clicked:
//close the page
//wait till the page is closed and fade dashboard back in
$('.r-close-button').click(function(){
$(this).parent().addClass('slidePageLeft')
.one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) {
$(this).removeClass('slidePageLeft').removeClass('openpage');
});
showDashBoard();
});
$('.s-close-button').click(function(){
$(this).parent().removeClass('slidePageInFromLeft').addClass('slidePageBackLeft');
showDashBoard();
});
})();
```
<p>And that's it!<br /> I hope you enjoyed this tutorial and found it useful! :)</p>
</pre></pre>