<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>CSS-Tricks</title>
	
	<link>https://css-tricks.com</link>
	<description>Tips, Tricks, and Techniques on using Cascading Style Sheets.</description>
	<lastBuildDate>Sat, 14 Jul 2018 23:23:56 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.6</generator>
<site xmlns="com-wordpress:feed-additions:1">45537868</site>	<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/CssTricks" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="csstricks" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>The div that looks different in every browser</title>
		<link>https://twitter.com/Martijn_Cuppens/status/1015169981368225793</link>
		<comments>https://css-tricks.com/the-div-that-looks-different-in-every-browser/#respond</comments>
		<pubDate>Fri, 13 Jul 2018 21:14:04 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Link]]></category>
		<category><![CDATA[outline]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273862</guid>
		<description><![CDATA[<p>It's not that Martijn Cuppens used User Agent sniffing, CSS hacks, or anything like that to make <a href="https://twitter.com/Martijn_Cuppens/status/1015169981368225793">this quirk div</a>. This is just a plain ol' <code>&#60;div&#62;</code> using the <a href="https://css-tricks.com/almanac/properties/o/outline/"><code>outline</code></a> property a la: </p>
<p><code class="language-css">div {<br />
  outline: inset 100px green;<br />
  outline-offset: -125px;<br />
}</code></p>
<p>It looks different in different browsers because browsers literally render something differently in this strange situation.</p>
<p>I happened upon Reddit user spidermonk33's comment in which they animated the offset to understand it a bit more. I took &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/the-div-that-looks-different-in-every-browser/">The div that looks different in every browser</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>It's not that Martijn Cuppens used User Agent sniffing, CSS hacks, or anything like that to make <a href="https://twitter.com/Martijn_Cuppens/status/1015169981368225793">this quirk div</a>. This is just a plain ol' <code>&lt;div&gt;</code> using the <a href="https://css-tricks.com/almanac/properties/o/outline/"><code>outline</code></a> property a la: </p>
<pre rel="CSS"><code class="language-css">div {
  outline: inset 100px green;
  outline-offset: -125px;
}</code></pre>
<p>It looks different in different browsers because browsers literally render something differently in this strange situation.</p>
<p>I happened upon Reddit user spidermonk33's comment in which they animated the offset to understand it a bit more. I took that idea and made this little video to show them behaving even weirder than the snapshots... <span id="more-273862"></span></p>
<figure><video src="http://res.cloudinary.com/css-tricks/video/upload/f_auto,q_auto:best/v1531244315/div_fazhfu.mp4" controls></video></figure>
<p><a href="https://twitter.com/Martijn_Cuppens/status/1015169981368225793" title="Direct link to featured article">Direct Link to Article</a> &#8212; <a href="https://css-tricks.com/the-div-that-looks-different-in-every-browser/">Permalink</a></p><p>The post <a rel="nofollow" href="https://css-tricks.com/the-div-that-looks-different-in-every-browser/">The div that looks different in every browser</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/the-div-that-looks-different-in-every-browser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273862</post-id>	</item>
		<item>
		<title>Scrolling Gradient</title>
		<link>https://codepen.io/MadeByMike/pen/eKPZZz</link>
		<comments>https://css-tricks.com/scrolling-gradient/#respond</comments>
		<pubDate>Fri, 13 Jul 2018 21:13:40 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Link]]></category>
		<category><![CDATA[gradients]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273845</guid>
		<description><![CDATA[<p>If you want a gradient that changes as you scroll down a very long page, you can create a gradient with a bunch of color stops, apply it to the body and it will do just that. </p>
<p>But, what if you don't want a perfectly vertical gradient? Like you want just the top left corner to change color? Mike Riethmuller, re-using his own technique from <a href="https://codepen.io/MadeByMike/pen/ZOrEmr">the CSS-only scroll indicator</a> (A-grade CSS trickery), did this by overlapping two gradients. The "top" &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/scrolling-gradient/">Scrolling Gradient</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>If you want a gradient that changes as you scroll down a very long page, you can create a gradient with a bunch of color stops, apply it to the body and it will do just that. </p>
<p>But, what if you don't want a perfectly vertical gradient? Like you want just the top left corner to change color? Mike Riethmuller, re-using his own technique from <a href="https://codepen.io/MadeByMike/pen/ZOrEmr">the CSS-only scroll indicator</a> (A-grade CSS trickery), did this by overlapping two gradients. The "top" one is fixed position and sort of leaves a hole that another taller gradient peeks through from below on scroll.</p>
<p><a href="https://codepen.io/MadeByMike/pen/eKPZZz" title="Direct link to featured article">Direct Link to Article</a> &#8212; <a href="https://css-tricks.com/scrolling-gradient/">Permalink</a></p><p>The post <a rel="nofollow" href="https://css-tricks.com/scrolling-gradient/">Scrolling Gradient</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/scrolling-gradient/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273845</post-id>	</item>
		<item>
		<title>Anatomy of a malicious script: how a website can take over your browser</title>
		<link>https://css-tricks.com/anatomy-of-a-malicious-script-how-a-website-can-take-over-your-browser/</link>
		<comments>https://css-tricks.com/anatomy-of-a-malicious-script-how-a-website-can-take-over-your-browser/#comments</comments>
		<pubDate>Fri, 13 Jul 2018 13:52:45 +0000</pubDate>
		<dc:creator><![CDATA[Paolo Mioni]]></dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273686</guid>
		<description><![CDATA[<p>By now, we all know that the major tech behemoths like Facebook or Google know everything about our lives, including how often we go to the bathroom (hence all the prostate medication ads that keep popping up, even on reputable news sites). After all, we’ve given them permission to do so, by reading pages and pages of legalese in their <abbr title="Terms and Conditions">T&#038;C</abbr> pages (we all did, didn’t we?) and clicking on the "Accept" button.</p>
<p>But what can a site do to &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/anatomy-of-a-malicious-script-how-a-website-can-take-over-your-browser/">Anatomy of a malicious script: how a website can take over your browser</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>By now, we all know that the major tech behemoths like Facebook or Google know everything about our lives, including how often we go to the bathroom (hence all the prostate medication ads that keep popping up, even on reputable news sites). After all, we’ve given them permission to do so, by reading pages and pages of legalese in their <abbr title="Terms and Conditions">T&C</abbr> pages (we all did, didn’t we?) and clicking on the "Accept" button.</p>
<p>But what can a site do to you, or to your device, without your explicit consent? What happens when you visit a slightly "improper" site, or a "proper" site you visited includes some third-party script that hasn’t been thoroughly checked?</p>
<p><span id="more-273686"></span></p>
<p>Has it ever happened to you that your browser gets hijacked and innumerable pop-ups come up, and you seem to be unable to close them without quitting the browser altogether, or clicking 25 times on the "Back" button? You do feel in danger when that happens, don’t you? </p>
<p>Following input from Chris here at CSS-Tricks, I decided to look for a script that does exactly that, and see what happens under the hood. It looked like a fairly daunting task, but I’ve learned quite a few things from it, and in the end had a lot of fun doing it. I hope I can share some of the fun with you.</p>
<h3>The hunt for the script</h3>
<p>The idea was to look for, to quote Chris, "bits of JavaScript that do surprisingly scary things."</p>
<p>The first thing I did was to set up a Virtual Machine with <a href="https://www.virtualbox.org/">Virtual Box</a> on my main Ubuntu development PC. This way, if the sites I visited and the scripts contained therein tried to do something scary to my computer, I would just need to erase the VM without compromising my precious laptop. I installed the latest version of Ubuntu on the VM, opened the browser and went hunting.</p>
<p>One of the things I was looking for was uses of a variation of the infamous <em><a href="https://samy.pl/evercookie/">Evercookie</a></em> (aka "undeletable cookie") which would be a clear sign of shady tracking techniques.</p>
<p>Where to look for such a script? I tried to find one of the aforementioned intrusive ads on legitimate websites, but couldn’t find any. It seems that companies supplying ads have become much better in spotting suspicious scripts by automating the vetting process, I assume. </p>
<p>I tried some reputable news sites, to see if there was anything interesting, but all I found were tons and tons of standard tracking scripts (and JavaScript errors in the console logs). In these cases, most of what the scripts do is send data to a server, and since you have little way of knowing what the server’s actually doing with the data, it would have been very difficult to dissect them.</p>
<p>I then thought that the best place to look for "scary" stuff would be sites whose owners won’t risk a legal action if they do something "scary" to their users. Which means, basically, sites where the user is trying to do something bordering on the illegal to begin with. </p>
<p>I looked at some Pirate Bay proxies, with no luck. Then I decided to move over to sites offering links to illegal streaming of sporting events. I went through a couple of sites, looking carefully at the scripts they included with Chromium’s DevTools. </p>
<p>On a site offering, amongst others, illegal streaming of table tennis matches, I noticed (in the list of JavaScripts in the DevTools Network tab) amongst third-party libraries, standard UI scripts and the all-too-frequent duplicate inclusion of the Google Analytics library (ouch!), a strangely named script with no .js extension and just a number as an URL.</p>
<figure id="post-273687" class="align-none media-273687"><img src="https://css-tricks.com/wp-content/uploads/2018/07/script-1.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_800,f_auto,q_auto/v1530826737/script-1_ulgi0g.png 800w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_598,f_auto,q_auto/v1530826737/script-1_ulgi0g.png 598w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530826737/script-1_ulgi0g.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Our suspect, spotted in the Network tab.</figcaption></figure>
<p>I had a look at the seemingly infinite couple of lines of obfuscated code that constituted most of the script’s code, and found strings like <code>chromePDFPopunderNew</code>, <code>adblockPopup</code>, <code>flashFileUrl</code>, escaped <code>&lt;script&gt;</code>tags, and even a string containing an inline PDF. This looked like interesting stuff. The hunt was over! I downloaded the script to my computer, and started trying to make some sense of it.</p>
<p class="explanation">I am not explicitly disclosing the domains involved in this operation, since we’re interested in the sin here, not the sinner. However, I’ve deliberately left a way of determining at least the main URL the script is sending users to. If you manage to solve the riddle, send me a private message, and I’ll tell you if you guessed right!</p>
<h3>The script: deobfuscating and figuring out the configuration parameters</h3>
<h4>What the script looks like </h4>
<p>The script is obfuscated, both for security purposes and to ensure a faster download. It is made of a big IIFE (<a href="https://en.wikipedia.org/wiki/Immediately-invoked_function_expression">Immediately-invoked function expression</a>), which is a technique used to isolate a piece of JavaScript code from its surroundings. Context doesn’t get mixed up with other scripts, and there is no risk of namespace conflict between function or variable names in different scripts.</p>
<p>Here’s the beginning of the script. Note the beginning of the base64-encoded PDF on the last line:</p>
<figure id="post-273688" class="align-none media-273688"><img src="https://css-tricks.com/wp-content/uploads/2018/07/script-2.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_800,f_auto,q_auto/v1530826754/script-2_z5w0en.png 800w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_673,f_auto,q_auto/v1530826754/script-2_z5w0en.png 673w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_597,f_auto,q_auto/v1530826754/script-2_z5w0en.png 597w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_518,f_auto,q_auto/v1530826754/script-2_z5w0en.png 518w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_424,f_auto,q_auto/v1530826754/script-2_z5w0en.png 424w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_328,f_auto,q_auto/v1530826754/script-2_z5w0en.png 328w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530826754/script-2_z5w0en.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /></figure>
<p>And here’s the end of it:</p>
<figure id="post-273689" class="align-none media-273689"><img src="https://css-tricks.com/wp-content/uploads/2018/07/script-3.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_800,f_auto,q_auto/v1530826778/script-3_hywuof.png 800w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_652,f_auto,q_auto/v1530826778/script-3_hywuof.png 652w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_582,f_auto,q_auto/v1530826778/script-3_hywuof.png 582w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_510,f_auto,q_auto/v1530826778/script-3_hywuof.png 510w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_421,f_auto,q_auto/v1530826778/script-3_hywuof.png 421w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_324,f_auto,q_auto/v1530826778/script-3_hywuof.png 324w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530826778/script-3_hywuof.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /></figure>
<p>The only action carried out in the global context, apparently, is to set the global variable <code>zfgloadedpopup</code>to true, presumably to tell other scripts belonging to the same "family" that this one has already been loaded. This variable is used only once, so the script itself does not check if it has loaded. So, if the site you’re visiting includes it twice by mistake, you’ll get double the pop-ups at the same price. Lucky!</p>
<p>The big IFEE expects two parameters, called <code>options</code> and <code>lary</code>. I actually checked the name of the second parameter to see what it might mean, and the only meaning I found was "aggressive, antisocial" in British slang. "So, we’re being aggressive here," I thought. "Interesting." </p>
<p>The<code>options</code> parameter is clearly an object with keys and values, even though they’re totally unintelligible. The <code>lary</code> parameter is a string of some sort. To make sense of this, the only option was to deobfuscate the whole script. Keep reading, and everything will be explained.</p>
<h4>Deobfuscating the script</h4>
<p>I first tried to resort to existing tools, but none of the online tools available seemed to be doing what I expected them to do. Most of what they did was pretty-printing the code, which my IDE can do quite easily by itself. I read about <a href="">JSDetox</a>, which is actual computer software and should be very helpful to debug this kind of script. However, I tried to install it into two different versions of Ubuntu and ended up in Ruby GEM dependency hell in both cases. JSDetox is fairly old, and I guess it’s practically abandonware now. The only option left was to go through things mostly by hand or via manual or semi-automated Regular Expression substitutions. I had to go through several steps to fully decipher the script.</p>
<p>Here is an animated GIF showing the same code section at various stages of deciphering:</p>
<figure id="post-273690" class="align-none media-273690"><img src="https://css-tricks.com/wp-content/uploads/2018/07/script-4.gif" alt="" /><figcaption>The transformation from unreadable gibberish to intelligible code.</figcaption></figure>
<p>The first step was quite straightforward: it required reformatting the code of the script, to add spacing and line breaks. I was left with properly indented code, but it was still full of very unreadable stuff, like the following:</p>
<pre rel="JavaScript"><code class="language-javascript">var w6D0 = window;
for (var Z0 in w6D0) {
  if (Z0.length === ((129.70E1, 0x1D2) &lt; 1.237E3 ? (47, 9) : (0x1CE, 1.025E3) &lt; (3.570E2, 122.) ? (12.9E1, true) : (5E0, 99.) &gt; 0x247 ? true : (120.7E1, 0x190)) &amp;&amp; Z0.charCodeAt((0x19D &gt; (0x199, 1.5E1) ? (88., 6) : (57., 0x1D9))) === (121.30E1 &gt; (1.23E2, 42) ? (45.2E1, 116) : (129., 85) &gt; (87., 5.7E2) ? (45.1E1, 0x4) : (103., 0x146) &gt;= (0x17D, 6.19E2) ? (1.244E3, 80) : (1.295E3, 149.)) &amp;&amp; Z0.charCodeAt(((1.217E3, 90.10E1) &lt;= (0xC2, 128.) ? (66, 'sw') : (0x25, 0xAB) &gt; 1.26E2 ? (134, 8) : (2.59E2, 0x12) &gt; 0xA9 ? 'sw' : (0x202, 0x20F))) === ((95, 15) &lt;= 63 ? (0x10B, 114) : (0xBB, 8.72E2) &lt;= (62, 51.) ? 'r' : (25, 70.) &gt;= (110.4E1, 0x8D) ? (121, 72) : (42, 11)) &amp;&amp; Z0.charCodeAt(((96.80E1, 4.7E1) &gt;= 62. ? (25.70E1, 46) : 0x13D &lt; (1.73E2, 133.1E1) ? (0x1A4, 4) : (28, 0x1EE) &lt;= 36.30E1 ? 37 : (14.61E2, 0x152))) === (81. &gt; (0x1FA, 34) ? (146, 103) : (0x8A, 61)) &amp;&amp; Z0.charCodeAt(((92.60E1, 137.6E1) &gt; (0x8, 0x3F) ? (123., 0) : (1.41E2, 12.11E2))) === ((0xA, 0x80) &gt; (19, 2.17E2) ? '' : (52, 0x140) &gt; (80., 0x8E) ? (42, 110) : 83.2E1 &lt;= (0x69, 0x166) ? (41., 'G') : (6.57E2, 1.093E3))) break
}
;</code></pre>
<p>What is this code doing? The only solution was to try and execute the code in a console and see what happened. As it turns out, this code loops through all of<code>window</code>’s properties and breaks out of the loop when that very complicated condition makes a match. The end result is sort of funny because all the code above does is the following:</p>
<pre rel="JavaScript"><code class="language-javascript">var Z0 = 'navigator'</code></pre>
<p>…that is, saving the <code>navigator</code> property of <code>window</code> to a variable called <code>Z0</code>. This is indeed a lot of effort just to assign a variable! There were several variables obfuscated like this, and after a few rounds of execution in the console, I managed to obtain the following global variables:</p>
<pre rel="JavaScript"><code class="language-javascript">var Z0 = 'navigator';
var Q0 = 'history';
var h0 = 'window'; // see comment below
/* Window has already been declared as w6D0. This is used to call the Window object of a variable containing a reference to a different window, other than the current one */</code></pre>
<p>The same could be applied to several other global variables declared at the beginning of the script. This whole shenanigan seemed a bit silly to me, since many other variables in the script are declared more openly a few lines later, like these:</p>
<pre rel="JavaScript"><code class="language-javascript">var m7W = {'K2': 'documentElement',
'W0': 'navigator',
'A2': 'userAgent',
'o2': 'document'};</code></pre>
<p>But never mind. After this procedure, I was left with a series of variables that are global to the script and are used all over it. </p>
<p>Time for some mass substitutions. I substituted the <code>w6D0</code>variable with <code>window</code> everywhere then proceeded with the other global variables.</p>
<p>Remember the variable <code>h0</code> above? It’s everywhere, used in statements like the following:</p>
<pre rel="JavaScript"><code class="language-javascript">if (typeof w6D0[h0][H8] == M3) {</code></pre>
<p>...which, after substitution, became:</p>
<pre rel="JavaScript"><code class="language-javascript">if (typeof window['window'][H8] == M3) {</code></pre>
<p>This is not much clearer than before, but still is a small step ahead from where I started. Similarly, the following line:</p>
<pre rel="JavaScript"><code class="language-javascript">var p = w6D0[X0][H](d3);</code></pre>
<p>...became this:</p>
<pre rel="JavaScript"><code class="language-javascript">var p = window["document"][H](d3);</code></pre>
<p>In the obfuscation technique used for this script, the names of variables that are local to a function are usually substituted with names with a single letter, like this:</p>
<pre rel="JavaScript"><code class="language-javascript">function D9(O, i, p, h, j) {
  var Q = 'newWin.opener = null;', Z = 'window.parent = null;', u = ' = newWin;', N = 'window.parent.',
    w = '' + atob('Ig==') + ');', g = '' + atob('Ig==') + ', ' + atob('Ig==') + '',
    f = 'var newWin = window.open(' + atob('Ig==') + '', d = 'window.frameElement = null;',
    k = 'window.top = null;', r = 'text', l = 'newWin_', F = 'contentWindow', O9 = 'new_popup_window_',
    I = 'disableSafeOpen', i9 = e['indexOf']('MSIE') !== -'1';
// more function code here
}</code></pre>
<p>Most global variables names, however, have been substituted with names with multiple letters, and all these names are unique. This means that it was possible for me to substitute them globally all over the script.</p>
<p> There was another big bunch of global variables:</p>
<pre rel="JavaScript"><code class="language-javascript">var W8 = 'plugins', f7 = 'startTimeout', z1 = 'attachEvent', b7 = 'mousemove', M1 = 'noScrollPlease',
  w7 = 'isOnclickDisabledInKnownWebView', a1 = 'notificationsUrl', g7 = 'notificationEnable', m8 = 'sliderUrl',
  T8 = 'interstitialUrl', v7 = '__interstitialInited', C8 = '%22%3E%3C%2Fscript%3E',
  O8 = '%3Cscript%20defer%20async%20src%3D%22', i8 = 'loading', p8 = 'readyState', y7 = '__pushupInited',
  o8 = 'pushupUrl', G7 = 'mahClicks', x7 = 'onClickTrigger', J7 = 'p', r7 = 'ppu_overlay', d7 = 'PPFLSH',
  I1 = 'function', H7 = 'clicksSinceLastPpu', k7 = 'clicksSinceSessionStart', s7 = 'lastPpu', l7 = 'ppuCount',
  t7 = 'seriesStart', e7 = 2592000000, z7 = 'call', Y1 = '__test', M7 = 'hostname', F1 = 'host',
  a7 = '__PPU_SESSION_ON_DOMAIN', I7 = 'pathname', Y7 = '__PPU_SESSION', F7 = 'pomc', V7 = 'ActiveXObject',
  q7 = 'ActiveXObject', c7 = 'iOSClickFix',
  m7 = 10802, D8 = 'screen',
// ... and many more</code></pre>
<p>I substituted all of those as well, with an automated script, and many of the functions became more intelligible. Some even became perfectly understandable without further work. A function, for example, went from this:</p>
<pre rel="JavaScript"><code class="language-javascript">function a3() {
  var W = E;
  if (typeof window['window'][H8] == M3) {
    W = window['window'][H8];
  } else {
    if (window["document"][m7W.K2] &amp;&amp; window["document"][m7W.K2][q5]) {
      W = window["document"][m7W.K2][q5];
    } else {
      if (window["document"][z] &amp;&amp; window["document"][z][q5]) {
        W = window["document"][z][q5];
      }
    }
  }
  return W;
}</code></pre>
<p>...to this:</p>
<pre rel="JavaScript"><code class="language-javascript">function a3() {
  var W = 0;
  if (typeof window['window']['innerWidth'] == 'number') {
    W = window['window']['innerWidth'];
  } else {
    if (window["document"]['documentElement'] &amp;&amp; window["document"]['documentElement']['clientWidth']) {
      W = window["document"]['documentElement']['clientWidth'];
    } else {
      if (window["document"]['body'] &amp;&amp; window["document"]['body']['clientWidth']) {
        W = window["document"]['body']['clientWidth'];
      }
    }
  }
  return W;
}</code></pre>
<p>As you can see, this function tries to determine the width of the client window, using all available cross-browser options. This might seem a bit overkill, since <code>window.innerWidth</code> is supported by all browsers starting from IE9. </p>
<p><code>window.document.documentElement.clientWidth</code>, however, works even in IE6; this shows us that our script tries to be as cross-browser compatible as it can be. We’ll see more about this later.</p>
<p>Notice how, to encrypt all of the property and function names used, this script makes heavy use of bracket notation, for example:</p>
<pre rel="JavaScript"><code class="language-javascript">window["document"]['documentElement']['clientWidth']</code></pre>
<p>...instead of:</p>
<pre rel="JavaScript"><code class="language-javascript">window.document.documentElement.clientWidth</code></pre>
<p>This allows the script to substitute the name of object methods and properties with random strings, which are then defined once — at the beginning of the script — with the proper method or property name. This makes the code very difficult to read, since you have to reverse all of the substitutions. It is obviously not only an obfuscation technique, however, since substituting long property names with one or two letters, if they occur often, can save quite a few bytes on the overall file size of the script and thus make it download faster.</p>
<p>The end result of the last series of substitutions I performed made the code even clearer, but I was still left with a very long script with a lot of functions with unintelligible names, like this one:</p>
<pre rel="JavaScript"><code class="language-javascript">function k9(W, O) {
  var i = 0, p = [], h;
  while (i &lt; W.length) {
    h = O(W[i], i, W);
    if (h !== undefined) {
      p['push'](h);
    }
    i += '1';
  }
  return p;
}</code></pre>
<p>All of them have variable declarations at the beginning of each function, most likely the result of the obfuscation/compression technique used on the original code. It is also possible that the writer(s) of this code were very scrupulous and declared all variables at the beginning of each function, but I have some doubts about that. </p>
<p>The <code>k9</code> function above is used diffusely in the script, so it was among the first I had to tackle. It expects two arguments, <code>W</code> and <code>O</code> and prepares a return variable (<code>p</code>) initialized as an empty array as well as a temporary variable (<code>h</code>). </p>
<p>Then it cycles through<code>W</code> with a <code>while</code> loop:</p>
<pre rel="JavaScript"><code class="language-javascript">while (i &lt; W.length) {</code></pre>
<p>This tells us that the <code>W</code> argument will be an array, or at least something traversable like an object or a string. It then supplies the current element in the loop, the current index of the loop, and the whole <code>W</code> argument as parameters to the initial<code>O</code> argument, which tells us the latter will be a function of some sort. It stores the result of the function’s execution in the temporary variable<code>h</code>:</p>
<pre rel="JavaScript"><code class="language-javascript">h = O(W[i], i, W);</code></pre>
<p>If the result of this function is not <code>undefined</code>, it gets appended to the result array <code>p</code>:</p>
<pre rel="JavaScript"><code class="language-javascript">if (h !== undefined) {
  p['push'](h);
}</code></pre>
<p>The returned variable is <code>p</code>.</p>
<p>What kind of construct is this? It’s obviously a mapping/filter function but is not just mapping the initial object <code>W</code>, since it does not return all of its values, but rather selects some of them. It is also not only filtering them, because it does not simply check for <code>true</code> or <code>false</code> and return the original element. It is sort of a hybrid of both. </p>
<p>I had to rename this function, just like I did with most of the others, giving a name that was easy to understand and explained the purpose of the function.</p>
<p>Since this function is usually used in the script to transform the original object <code>W</code> in one way or another, I decided to rename it <code>mapByFunction</code>. Here it is, in its un-obfuscated glory:</p>
<pre rel="JavaScript"><code class="language-javascript">function mapByFunction(myObject, mappingFunction) {
  var i = 0, result = [], h;
  while (i &lt; myObject.length) {
    h = mappingFunction(myObject[i], i, myObject);
    if (h !== undefined) {
      result['push'](h);
    }
    i += 1;
  }
  return result;
}</code></pre>
<p>A similar procedure had to be applied to all of the functions in the script, trying to guess one by one what they were trying to achieve, what variables were passed to them and what they were returning. In many cases, this involved going back and forth in the code when one function I was deciphering was using another function I hadn’t deciphered yet.</p>
<p>Some other functions were nested inside others, because they were used only in the context of the enclosing function, or because they were part of some third-party piece of code that had been pasted verbatim within the script.<br />
At the end of all this tedious work, I had a big script full of fairly intelligible functions, all with nice descriptive (albeit very long) names.</p>
<p>Here are some of the names, from the <em>Structure</em> panel of my <abbr title="Integrated development environment">IDE</abbr>:</p>
<figure id="post-273691" class="align-none media-273691"><img src="https://css-tricks.com/wp-content/uploads/2018/07/script-5.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_528,f_auto,q_auto/v1530826847/script-5_spqjbg.png 528w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530826847/script-5_spqjbg.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>List of deciphered functions with descriptive names.</figcaption></figure>
<p>Now that the functions have names, you can start guessing a few of the things this script is doing. Would any of you like to try to <code>injectPDFAndDoStuffDependingOnChromeVersion</code> in someone’s browser now?</p>
<h4>Structure of the script</h4>
<p>Once the individual functions comprising the script had been deciphered, I tried to make a sense of the whole. </p>
<p>The script at the beginning is made out of a lot of helper functions, which often call other functions, and sometimes set variables in the global scope (yuck!). Then the main logic of the script begins, around line 1,680 of my un-obfuscated version.</p>
<p>The script can behave very differently depending on the configuration that gets passed to it: a lot of functions check one or several parameters in the main<code>options</code> argument, like this:</p>
<pre rel="JavaScript"><code class="language-javascript">if (options['disableSafeOpen'] || notMSIE) {
  // code here
}</code></pre>
<p>Or like this:</p>
<pre rel="JavaScript"><code class="language-javascript">if (!options['disableChromePDFPopunderEventPropagation']) {
  p['target']['click']();
}</code></pre>
<p>But the <code>options</code> argument, if you remember, is encrypted. So the next thing to do was to decipher it.</p>
<h4>Decrypting the configuration parameters</h4>
<p>At the very beginning of the script’s main code, there’s this call:</p>
<pre rel="JavaScript"><code class="language-javascript">// decode options;
if (typeof options === 'string') {
  options = decodeOptions(options, lary);
}</code></pre>
<p class="explanation"><code>decodeOptions</code> is the name I gave to the function that performs the job. It was originally given the humble name <code>g4</code>.</p>
<p>Finally, we’re also using the mysterious<code>lary</code> argument, whose value is:</p>
<pre rel="JavaScript"><code class="language-javascript">"abcdefghijklmnopqrstuvwxyz0123456789y90x4wa5kq72rftj3iepv61lgdmhbn8ouczs"</code></pre>
<p>The first half of the string is clearly the alphabet in lower letters, followed by numbers 0 through 9. The second half consists of random characters. Does that look like a cypher to you? If your answer is yes, you are damn right. It is, in fact, a <a href="https://en.wikipedia.org/wiki/Substitution_cipher">simple substitution cypher</a>, with a little twist.</p>
<p>The whole <code>decodeOptions</code> function looks like this:</p>
<pre rel="JavaScript"><code class="language-javascript">function decodeOptions(Options, lary) {
  var p = ')',
    h = '(',
    halfLaryLength = lary.length / 2,
    firstHalfOfLary = lary['substr'](0, halfLaryLength),
    secondHalfOfLary = lary['substr'](halfLaryLength),
    w,
    // decrypts the option string before JSON parsing it
    g = mapByFunction(Options, function (W) {
      w = secondHalfOfLary['indexOf'](W);
      return w !== -1 ? firstHalfOfLary[w] : W;
    })['join']('');
  if (window['JSON'] &amp;&amp; window['JSON']['parse']) {
    try {
      return window['JSON']['parse'](g);
    } catch (W) {
      return eval(h + g + p);
    }
  }
  return eval(h + g + p);
}</code></pre>
<p>It first sets a couple of variables containing opening and closing parentheses, which will be used later:</p>
<pre rel="JavaScript"><code class="language-javascript">var p = ')',
    h = '(',</code></pre>
<p>Then it splits our <code>lary</code> argument in half:</p>
<pre rel="JavaScript"><code class="language-javascript">halfLaryLength = lary.length / 2,
firstHalfOfLary = lary['substr'](0, halfLaryLength),
secondHalfOfLary = lary['substr'](halfLaryLength),</code></pre>
<p>Next, It maps the <code>Options</code> string, letter by letter, with this function:</p>
<pre rel="JavaScript"><code class="language-javascript">function (W) {
  w = secondHalfOfLary['indexOf'](W);
  return w !== -1 ? firstHalfOfLary[w] : W;
}</code></pre>
<p>If the current letter is present in the second half of the <code>lary</code> argument, it returns the corresponding letter in the lower-case alphabet in the first part of the same argument. Otherwise, it returns the current letter, unchanged. This means that the <em>options</em> parameter is only half encrypted, so to say. </p>
<p>Once the mapping has taken place, the resulting array of decrypted letters <code>g</code> (remember, <code>mapByFunction</code> always returns an array) is then converted again to a string:</p>
<pre rel="JavaScript"><code class="language-javascript">g['join']('')</code></pre>
<p>The configuration is initially a JSON object, so the script tries to use the browser’s native JSON.parse function to turn it into an object literal. If the JSON object is not available (IE7 or lower, Firefox and Safari 3 or lower), it resorts to putting it between parentheses and evaluating it:</p>
<pre rel="JavaScript"><code class="language-javascript">if (window['JSON'] &amp;&amp; window['JSON']['parse']) {
  try {
    return window['JSON']['parse'](g);
  } catch (W) {
    return eval(h + g + p);
  }
}
return eval(h + g + p);</code></pre>
<p>This is another case of the script being extremely cross-browser compatible, to the point of supporting browsers that are more than 10 years old. I’ll try and explain why in a while.</p>
<p>So, now the <code>options</code> variable has been decrypted. Here it is in all its deciphered splendour, albeit with the original URLs omitted:</p>
<pre rel="JavaScript"><code class="language-javascript">let options = {
  SS: true,
  adblockPopup: true,
  adblockPopupLink: null,
  adblockPopupTimeout: null,
  addOverlay: false,
  addOverlayOnMedia: true,
  aggressive: false,
  backClickAd: false,
  backClickNoHistoryOnly: false,
  backClickZone: null,
  chromePDFPopunder: false,
  chromePDFPopunderNew: false,
  clickAnywhere: true,
  desktopChromeFixPopunder: false,
  desktopPopunderEverywhere: false,
  desktopPopunderEverywhereLinks: false,
  disableChromePDFPopunderEventPropagation: false,
  disableOnMedia: false,
  disableOpenViaMobilePopunderAndFollowLinks: false,
  disableOpenViaMobilePopunderAndPropagateEvents: false,
  disablePerforamnceCompletely: false,
  dontFollowLink: false,
  excludes: [],
  excludesOpenInPopunder: false,
  excludesOpenInPopunderCapping: null,
  expiresBackClick: null,
  getOutFromIframe: false,
  iOSChromeSwapPopunder: false,
  iOSClickFix: true,
  iframeTimeout: 30000,
  imageToTrackPerformanceOn: "", /* URL OMITTED */
  includes: [],
  interstitialUrl: "", /* URL OMITTED */
  isOnclickDisabledInKnownWebView: false,
  limLo: false,
  mahClicks: true,
  mobilePopUpTargetBlankLinks: false,
  mobilePopunderTargetBlankLinks: false,
  notificationEnable: false,
  openPopsWhenInIframe: false,
  openViaDesktopPopunder: false,
  openViaMobilePopunderAndPropagateFormSubmit: false,
  partner: "pa",
  performanceUrl: "", /* URL OMITTED */
  pomc: false,
  popupThroughAboutBlankForAdBlock: false,
  popupWithoutPropagationAnywhere: false,
  ppuClicks: 0,
  ppuQnty: 3,
  ppuTimeout: 25,
  prefetch: "",
  resetCounters: false,
  retargetingFrameUrl: "",
  scripts: [],
  sessionClicks: 0,
  sessionTimeout: 1440,
  smartOverlay: true,
  smartOverlayMinHeight: 100,
  smartOverlayMinWidth: 450,
  startClicks: 0,
  startTimeout: 0,
  url: "", /* URL OMITTED */
  waitForIframe: true,
  zIndex: 2000,
  zoneId: 1628975
}</code></pre>
<p>I found the fact that there is an <code>aggressive</code> option very interesting, even though this option is unfortunately not used in the code. Given all the things this script does to your browser, I was very curious what it would have done had it been more "aggressive."</p>
<p>Not all of the options passed to the script are actually used in the script; and not all of the options the script checks are present in the <code>options</code> argument passed in this version of it. I assume that some of the options that are not present in the script’s configuration are used in versions deployed onto other sites, especially for cases in which this script is used on multiple domains. Some options might also be there for legacy reasons and are simply not in use anymore. The script has some empty functions left in, which likely used some of the missing options.</p>
<h3>What does the script actually do?</h3>
<p>Just by reading the name of the options above, you can guess a lot of what this script does: it will open a <code>smartOverlay</code>, even using a special <code>adblockPopup</code>. If you <code>clickAnywhere</code>, it will open a <code>url</code>. In our specific version of the script, it will not <code>openPopsWhenInIframe</code>, and it will not <code>getOutFromIframe</code>, even though it will apply an <code>iOSClickFix</code>. It will count popups and save the value in <code>ppuCount</code>, and even track performance using an <code>imageToTrackPerformanceOn</code> (which I can tell you, even if I omitted the URL, is hosted on a CDN). It will track <code>ppuClicks</code> (pop up clicks, I guess), and cautiously limit itself to a <code>ppuQnty</code> (likely a pop up quantity).</p>
<p>By reading the code, I could find out a lot more, obviously. Let’s see what the script does and follow its logic. I will try to describe all of the interesting things it can do, including those that are not triggered by the set of options I was able to decipher.</p>
<p>The main purpose of this script is to direct the user to a URL that is stored in its configuration as <code>options['url']</code>. The URL in the configuration I found redirected me to a very spammy website, so I will refer to this this URL as <em>Spammy Site</em> from now on, for the sake of clarity.</p>
<h4>1. I want to get out of this iFrame!</h4>
<p>The first thing this script does is try to get a reference to the top window if the script itself is run from within in an iFrame and, if the current configuration requires it, sets that as the main window on which to operate, and sets all reference to the document element and user agent to those of the top window:</p>
<pre rel="JavaScript"><code class="language-javascript">if (options['getOutFromIframe'] &amp;&amp; iframeStatus === 'InIframeCanExit') {
  while (myWindow !== myWindow.top) {
    myWindow = myWindow.top;
  }
  myDocument = myWindow['document'];
  myDocumentElement = myWindow['document']['documentElement'];
  myUserAgent = myWindow['navigator']['userAgent'];
}</code></pre>
<h4>2. What’s your browser of choice?</h4>
<p>The second thing it does is a very minute detection of the current browser, browser version and operating system by parsing the user agent string. It detects if the user is using Chrome and its specific version, Firefox, Firefox for Android, UC Browser, Opera Mini, Yandex, or if the user is using the Facebook app. Some checks are very specific:</p>
<pre rel="JavaScript"><code class="language-javascript">isYandexBrowser = /YaBrowser/['test'](myUserAgent),
isChromeNotYandex = chromeVersion &amp;&amp; !isYandexBrowser,</code></pre>
<p>We’ll see why later.</p>
<h4>3. All your browser are belong to us.</h4>
<figure id="post-273794" class="align-none media-273794"><img src="https://css-tricks.com/wp-content/uploads/2018/07/all_your_browser-300x225.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_640,f_auto,q_auto/v1531123570/all_your_browser_qokcab.png 640w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1531123570/all_your_browser_qokcab.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Original meme <a href="https://en.wikipedia.org/wiki/All_your_base_are_belong_to_us" target="_blank">here</a>.</figcaption></figure>
<p>The first disturbing thing the script does is check for the presence of the <code>history.pushState()</code> function, and if it is present, the script injects a fake history entry with the current url’s title. This allows it to intercept back click events (using the <code>popstate</code> event) and send the user to the Spammy Site instead of the previous page the user actually visited. If it hadn’t added a fake history entry first, this technique would not work.</p>
<pre rel="JavaScript"><code class="language-javascript">function addBackClickAd(options) {

  if (options['backClickAd'] &amp;&amp; options['backClickZone'] &amp;&amp; typeof window['history']['pushState'] === 'function') {
    if (options['backClickNoHistoryOnly'] &amp;&amp; window['history'].length &gt; 1) {
      return false;
    }
    // pushes a fake history state with the current doc title
    window['history']['pushState']({exp: Math['random']()}, document['title'], null);
    var createdAnchor = document['createElement']('a');
    createdAnchor['href'] = options['url'];
    var newURL = 'http://' + createdAnchor['host'] + '/afu.php?zoneid=' + options['backClickZone'] + '&amp;var=' + options['zoneId'];
    setTimeout(function () {
      window['addEventListener']('popstate', function (W) {
        window['location']['replace'](newURL);
      });
    }, 0);
  }
}</code></pre>
<p>This technique is used only outside the context of an iFrame, and not on Chrome iOS and UC Browser.</p>
<h4>4. This browser needs more scripts</h4>
<p>If one malicious script was not enough, the script tries to inject more scripts, depending on the configuration. All scripts are appended to the <code>&lt;head&gt;</code> of the document, and may include something that is called either an Interstitial, a Slider, or a Pushup, all of which I assume are several forms of intrusive ads that get shown to the browser. I could not find out because, in our script’s case, the configuration did not contain any of those, apart from one which was a dead URL when I checked it.</p>
<h4>5. Attack of the click interceptor</h4>
<p>Next, the script attaches a "click interceptor" function to all types of click events on the document, including touch events on mobile. This function intercepts all user clicks or taps on the document, and proceeds to open different types of pop-ups, using different techniques depending on the device.</p>
<p>In some cases it tries to open a "popunder." This means that it intercepts any click on a link, reads the original link destination, opens that link in the current window, and  opens a new window with the Spammy Site in it at the same time. In most cases, it proceeds to restore focus to the original window, instead of the new window it has created. I think this is meant to circumvent some browser security measures that check if something is changing URLs the user has actually clicked. The user will then find themselves with the correct link open, but with another tab with the Spammy Site in it, which the user will sooner or later see when changing tabs.</p>
<p>In other cases, the script does the opposite and opens a new window with the link the user has clicked on, but changes the current window’s URL to that of the Spammy Site. </p>
<p>To do all this, the script has different functions for different browsers, each presumably written to circumvent the security measures of each browser, including AdBlock if it is present. Here is some of the code doing this to give you an an idea:</p>
<pre rel="JavaScript"><code class="language-javascript">if (options['openPopsWhenInIframe'] &amp;&amp; iframeStatus === 'InIframeCanNotExit') {
  if (isIphoneIpadIpod &amp;&amp; (V || p9)) {
    return openPopunder(W);
  }
  return interceptEventAndOpenPopup(W);
}
if (options['adblockPopup'] &amp;&amp; currentScriptIsApuAfuPHP) {
  return createLinkAndTriggerClick(options['adblockPopupLink'], options['adblockPopupTimeout']);
}
if (options['popupThroughAboutBlankForAdBlock'] &amp;&amp; currentScriptIsApuAfuPHP) {
  return openPopup();
}
if (!isIphoneIpadIpodOrAndroid &amp;&amp; (options['openViaDesktopPopunder'] || t)) {
  if (isChromeNotYandex &amp;&amp; chromeVersion &gt; 40) {
    return injectPDFAndDoStuffDependingOnChromeVersion(W);
  }
  if (isSafari) {
    return openPopupAndBlank(W);
  }
  if (isYandexBrowser) {
    return startMobilePopunder(W, I);
  }
}
/* THERE ARE SEVERAL MORE LINES OF THIS KIND OF CODE */</code></pre>
<p>To give you an example of a browser-specific behavior, the script opens a new window with the Spammy Site in it on Safari for Mac, immediately opens a blank window, gives that focus and then immediately closes it:</p>
<pre rel="JavaScript"><code class="language-javascript">function openPopupAndBlank(W) {
  var O = 'about:blank';
  W['preventDefault']();

  // opens popup with options URL
  safeOpen(
    options['url'],
    'ppu' + new Date()['getTime'](),
    ['scrollbars=1', 'location=1', 'statusbar=1', 'menubar=0', 'resizable=1', 'top=0', 'left=0', 'width=' + window['screen']['availWidth'], 'height=' + window['screen']['availHeight']]['join'](','),
    document,
    function () {
      return window['open'](options['url']);
    }
  );
  // opens blank window, gives it focuses and closes it (??)
  var i = window['window']['open'](O);
  i['focus']();
  i['close']();
}</code></pre>
<p>After setting up click interception, it creates a series of "smartOverlays." These are layers using transparent GIFs for a background image, which are positioned above each of the <code>&lt;object&gt;</code>, <code>&lt;iframe&gt;</code>, <code>&lt;embed&gt;</code>, <code>&lt;video&gt;</code> and <code>&lt;audio&gt;</code> tags that are present in the original document, and cover them completely. This is meant to intercept all clicks on any media content and trigger the click interceptor function instead:</p>
<pre rel="JavaScript"><code class="language-javascript">if (options['smartOverlay']) {
  var f = [];
  (function d() {
    var Z = 750,
        affectedTags = 'object, iframe, embed, video, audio';
    mapByFunction(f, function (W) {
      if (W['parentNode']) {
        W['parentNode']['removeChild'](W);
      }
    });
    f = mapByFunction(safeQuerySelectorAll(affectedTags), function (W) {
      var O = 'px' 
      if (!checkClickedElementTag(W, true)) {
        return;
      }
      if (flashPopupId &amp;&amp; W['className'] === flashPopupId) {
        return;
      }
      if (options['smartOverlayMinWidth'] &lt;= W['offsetWidth'] &amp;&amp; options['smartOverlayMinHeight'] &lt;= W['offsetHeight']) {
        var Q = getElementTopAndLeftPosition(W);
        return createNewDivWithGifBackgroundAndCloneStylesFromInput({
          left: Q['left'] + O,
          top: Q.top + O,
          height: W['offsetHeight'] + O,
          width: W['offsetWidth'] + O,
          position: 'absolute'
        });
      }
    });
    popupTimeOut2 = setTimeout(d, Z);
  })();
}</code></pre>
<p>This way, the script is able to even intercept clicks on media objects that might not trigger standard "click" behaviors in JavaScript.</p>
<p>The script tries to do another couple of strange things. For example, on mobile devices, it tries to scan for links that point to a blank window and attempts to intercept them with a custom function. The function even temporarily manipulates the <code>rel</code> attribute of the links and sets it to a value of <code>'noopener noreferer'</code> before opening the new window. It is a strange thing to do since this is supposedly a <a href="https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/">security measure</a> for some older browsers. The idea may have been to avoid performance hits to the main page if the Spammy Site consumes too many resources and clogs the original page (something Jake Archibald <a href="https://jakearchibald.com/2016/performance-benefits-of-rel-noopener/">explains here</a>). However, this technique is used exclusively in this function and nowhere else, making it is a bit of a mystery to me.</p>
<p>The other strange thing the script does is to try and create a new window and add an iFrame with a PDF string as its source. This new window is immediately positioned off screen and the PDF iFrame is removed in case of a change of focus or visibility of the page. In some cases, only after the PDF has been removed does the script redirect to the Spammy Site. This feature seems to only target Chrome and I haven’t been able to determine whether the PDF is malicious or not.</p>
<h4>6. Tell me more about yourself</h4>
<p>Lastly, the script proceeds to collect a lot of information about the browser, which will be appended to the URL of the Spammy Site. It checks the following:</p>
<ul>
<li>if Flash is installed</li>
<li>the width and height of the screen, the current window, and the window’s position in respect to the screen</li>
<li>the number of iFrames in the top window</li>
<li>the current URL of the page</li>
<li>if the browser has plugins installed</li>
<li>if the browser is PhantomJs or Selenium WebDriver (presumably to check if the site is currently being visited by an automated browser of some sort, and probably do something less scary than usual since automated browsers are likely to be used by companies producing anti-virus software, or law enforcement agencies)</li>
<li>if the browser supports the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon"><code>sendBeacon</code></a> method of the<code>Navigator</code>object</li>
<li>if the browser supports geolocation</li>
<li>if the script is currently running in an iFrame</li>
</ul>
<p>It then adds these values to the Spammy Site’s URL, each encoded with its own variable. The Spammy Site will obviously use the information to resize its content according to the size of the browser window, and presumably also to adjust the level of the content’s maliciousness depending on whether the browser is highly vulnerable (for example, it has Flash installed) or is possibly an anti-spam bot (if it is detected as being an automated browser).</p>
<p>After this, the script is done. It does quite a few interesting things, doesn’t it?</p>
<h3>Techniques and cross-browser compatibility</h3>
<p>Let’s look at a few of techniques the script generally uses and why it needs them.</p>
<h4>Browser detection</h4>
<p>When writing code for the web, avoiding browser detection is typically accepted as a best practice because it is an error-prone technique: user agent strings are very complicated to parse and they can change with time as new browsers are released. I personally avoid browser detection on my projects like the plague.<br />
In this case, however, correct browser detection can mean the success or failure of opening the Spammy Site on the user’s computer. This is the reason why the script tries to detect the browser and OS as carefully as it can. </p>
<h4>Browser compatibility</h4>
<p>For the same reasons, the script uses a lot of cross-browser techniques to maximize compatibility. This might be a result of a very old script which has been updated many times over the years, while keeping all of the legacy code intact. But it might also be a case of trying to keep the script compatible with as many browsers as possible.</p>
<p>After all, for people who are possibly trying to install malware on unsuspecting users, a user who is browsing the web with a very outdated browser or even a newer browser with outdated plug-ins is much more vulnerable to attacks and is certainly a great find!</p>
<p>One example is the function the script uses to open new windows in all other functions, which I’ve renamed <code>safeOpen</code>:</p>
<pre rel="JavaScript"><code class="language-javascript">// SAFE OPEN FOR MSIE
function safeOpen(URLtoOpen, popupname, windowOptions, myDocument, windowOpenerFunction) {
  var notMSIE = myUserAgent['indexOf']('MSIE') !== -1;
  if (options['disableSafeOpen'] || notMSIE) {
    var W9 = windowOpenerFunction();
    if (W9) {
      try {
        W9['opener']['focus']();
      } catch (W) {
      }
      W9['opener'] = null;
    }
    return W9;
  } else {
    var t, c, V;
    if (popupname === '' || popupname == null) {
      popupname = 'new_popup_window_' + new Date()['getTime']();
    }
    t = myDocument['createElement']('iframe');
    t['style']['display'] = 'none';
    myDocument['body']['appendChild'](t);
    c = t['contentWindow']['document'];
    var p9 = 'newWin_' + new Date()['getTime']();
    V = c['createElement']('script');
    V['type'] = 'text/javascript';
    V['text'] = [
      'window.top = null;',
      'window.frameElement = null;',
      'var newWin = window.open(' + atob('Ig==') + '' + URLtoOpen + '' + atob('Ig==') + ', ' + atob('Ig==') + '' + popupname + '' + atob('Ig==') + ', ' + atob('Ig==') + '' + windowOptions + '' + atob('Ig==') + ');',
      'window.parent.' + p9 + ' = newWin;',
      'window.parent = null;',
      'newWin.opener = null;'
    ]['join']('');
    c['body']['appendChild'](V);
    myDocument['body']['removeChild'](t);
    return window[p9];
  }
}</code></pre>
<p>Every time this function is called, it is passes another function to run that opens a new window (it is the last argument passed to the function above, called <code>windowOpenerFunction</code>). This function is customized in each call depending on the specific need of the current use case. However, if the script detects that it’s running on Internet Explorer, and the <code>disableSafeOpen</code> option is not set to true, then it resorts to a fairly convoluted method to open the window using the other parameters (<code>URLtoOpen</code>, <code>popupname</code>, <code>windowOptions</code>, <code>myDocument)</code> instead of using the <code>windowOpenerFunction</code> function to open the new window. It creates an iFrame, inserts it into the current document, then adds a JavaScript script node to that iFrame, which opens the new window. Finally, it removes the iFrame that it just created.</p>
<h4>Catching all exceptions</h4>
<p>Another way this script always stays on the safe side is to catch exceptions, in fear that they will cause errors that might block JavaScript execution. Every time it calls a function or method that is not 100% safe on all browsers, it does so by passing it through a function that catches exceptions (and handles them if passed a handler, even though I haven’t spotted a use case where the exception handler is actually passed). I’ve renamed the original function <code>tryFunctionCatchException</code>, but it could have easily been called <code>safeExecute</code>:</p>
<pre rel="JavaScript"><code class="language-javascript">function tryFunctionCatchException(mainFunction, exceptionHandler) {
  try {
    return mainFunction();
  } catch (exception) {
    if (exceptionHandler) {
      return exceptionHandler(exception);
    }
  }
}</code></pre>
<h3>Where does this script lead?</h3>
<p>As you’ve seen, the script is configurable to redirect the user to a specific URL (the Spammy Site) which must be compiled in the semi-encrypted option for each individual version of this script that is deployed. This means the Spammy Site can be different for every instance of this script. In our case, the target site was some sort of Ad Server serving different pages, presumably based on an auction (the URL contained a parameter called <code>auction_id</code>).</p>
<p>When I first followed the link, it redirected me to what indeed was a very spammy site: it was advertising get-rich-quick schemes based on online trading, complete with pictures of a guy sitting in what was implied to be the new Lamborghini he purchased by getting rich with said scheme. The target site even used the Evercookie cookie to track users. </p>
<p>I recently re-ran the URL a few times, and it has redirected me to:</p>
<ul>
<li>a landing page belonging to a famous online betting company (which has been the official sponsor of at least one European Champions League finalist), complete with the usual "free betting credit"</li>
<li>several fake news sites, in Italian and in French</li>
<li>sites advertising "easy" weight-loss programs</li>
<li>sites advertising online cryptocurrency trading</li>
</ul>
<h4>Conclusion</h4>
<p>This is a strange script, in some respect. It seems that it has been created to take total control of the user’s browser, and to redirect the user to a specific target page. Theoretically, this script could arbitrarily inject other malicious scripts like keyloggers, cryptominers, etc., if it chose to. This kind of aggressive behavior (taking control of all links, intercepting all clicks on videos and other interactive elements, injecting PDFs, etc.) seems more typical of a malicious script that has been added to a website without the website owner’s consent.</p>
<p>However, after more than a month since I first found it, the script (in a slightly different version) is still there on the original website. It limits itself to intercepting every other click, keeping the original website at least partially usable. It is not that likely that the website’s original owner hasn’t noticed the presence of this script given that it’s been around this long. </p>
<p>The other strange thing is that this script points to what is, in all respects, an ad bidding service, though one that serves very spammy clients. There is at least one major exception: the aforementioned famous betting company. Is this script a malicious script which has evolved into some sort of half-legitimate ad serving system, albeit a very intrusive one? The Internet can be a very complicated place, and very often things aren’t totally legitimate or totally illegal — between black and white there are always several shades of grey.</p>
<p>The only advice I feel I can give you after analyzing this script is this: the next time you feel the irresistible urge to watch a table tennis match online, go to a legitimate streaming service and pay for it. It will save you a lot of hassles.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/anatomy-of-a-malicious-script-how-a-website-can-take-over-your-browser/">Anatomy of a malicious script: how a website can take over your browser</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/anatomy-of-a-malicious-script-how-a-website-can-take-over-your-browser/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273686</post-id>	</item>
		<item>
		<title>Design Systems at GitHub</title>
		<link>https://medium.com/@broccolini/design-systems-at-github-c8e5378d2542</link>
		<comments>https://css-tricks.com/design-systems-at-github/#respond</comments>
		<pubDate>Thu, 12 Jul 2018 16:53:21 +0000</pubDate>
		<dc:creator><![CDATA[Robin Rendle]]></dc:creator>
				<category><![CDATA[Link]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273858</guid>
		<description><![CDATA[<p>Here’s a nifty post by Diana Mounter all about the design systems team at GitHub that details how the team was formed, the problems they've faced and how they've adapted along the way:</p>
<p>When I started working at GitHub in late 2015, I noticed that there were many undocumented patterns, I had to write a lot of new CSS to implement designs, and that there weren’t obvious underlying systems connecting all the pieces together. I knew things could be better &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/design-systems-at-github/">Design Systems at GitHub</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Here’s a nifty post by Diana Mounter all about the design systems team at GitHub that details how the team was formed, the problems they've faced and how they've adapted along the way:</p>
<blockquote><p>When I started working at GitHub in late 2015, I noticed that there were many undocumented patterns, I had to write a lot of new CSS to implement designs, and that there weren’t obvious underlying systems connecting all the pieces together. I knew things could be better and I was enthusiastic about making improvements—I quickly discovered that I wasn’t the only one that felt this way. There were several folks working on efforts to improve things, but weren’t working together. With support from design leads, a group of us started to meet regularly to discuss improvements to Primer and prioritize work—this was the beginnings of the design systems team.</p></blockquote>
<p>This whole post had me nodding furiously along to every word but there was one point that I particularly made note of: the part where Diana mentions how her team decided to make “the status of styles more obvious” in order to communicate changes to the rest of the team. </p>
<p>Lately, I’ve noticed how design systems can demonstrate the status of a project, which is super neat. Communicating these large changes to the codebase <a href="https://css-tricks.com/prototyping-in-the-browser/">early and frequently</a>, over-communicating almost, is probably a good idea when a design systems team is just getting started.</p>
<p><a href="https://medium.com/@broccolini/design-systems-at-github-c8e5378d2542" title="Direct link to featured article">Direct Link to Article</a> &#8212; <a href="https://css-tricks.com/design-systems-at-github/">Permalink</a></p><p>The post <a rel="nofollow" href="https://css-tricks.com/design-systems-at-github/">Design Systems at GitHub</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/design-systems-at-github/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273858</post-id>	</item>
		<item>
		<title>Building a Complex UI Animation in React, Simply</title>
		<link>https://css-tricks.com/building-a-complex-ui-animation-in-react-simply/</link>
		<comments>https://css-tricks.com/building-a-complex-ui-animation-in-react-simply/#comments</comments>
		<pubDate>Thu, 12 Jul 2018 13:45:47 +0000</pubDate>
		<dc:creator><![CDATA[Alex Holachek]]></dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[navigation]]></category>
		<category><![CDATA[react]]></category>
		<category><![CDATA[stripe]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=272368</guid>
		<description><![CDATA[<p>Let’s use <a href="https://reactjs.org/">React</a>, <a href="https://www.styled-components.com/">styled-components</a>, and <a href=" https://github.com/aholachek/react-flip-toolkit">react-flip-toolkit</a> to make our own version of the animated navigation menu on the Stripe homepage. It's an impressive menu with some slick animation effects and the combination of these three tools can make it relatively easy to recreate.</p>
</p>
<p class="explanation">This is an intermediate-level walkthrough that assumes familiarity with React and basic animation concepts. Our <a href="https://css-tricks.com/guides/react/">React guide</a> is a good place to start.</p>
<p>Here's what we're aiming to make:</p>
<p data-height="750" data-theme-id="1" data-slug-hash="PaYXEZ" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/PaYXEZ/">React Stripe Menu</a>&#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/building-a-complex-ui-animation-in-react-simply/">Building a Complex UI Animation in React, Simply</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Let’s use <a href="https://reactjs.org/">React</a>, <a href="https://www.styled-components.com/">styled-components</a>, and <a href=" https://github.com/aholachek/react-flip-toolkit">react-flip-toolkit</a> to make our own version of the animated navigation menu on the Stripe homepage. It's an impressive menu with some slick animation effects and the combination of these three tools can make it relatively easy to recreate.</p>
<p><span id="more-272368"></span></p>
<p class="explanation">This is an intermediate-level walkthrough that assumes familiarity with React and basic animation concepts. Our <A href="https://css-tricks.com/guides/react/">React guide</a> is a good place to start.</p>
<p>Here's what we're aiming to make:</p>
<p data-height="750" data-theme-id="1" data-slug-hash="PaYXEZ" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/PaYXEZ/">React Stripe Menu</a> by Alex (<a href="https://codepen.io/aholachek">@aholachek</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<p><a class="button" href="https://github.com/aholachek/react-stripe-menu">View Repo</a></p>
<hr>
<h3>Breaking down the animation</h3>
<p>First, let's break down the animation into different parts so we can more easily reproduce it. You might want to check out the finished product in slow motion (use the toggles) so you can catch all the details.</p>
<ol>
<li>The white dropdown container updates both its size and position.</li>
<li>The gray background in the bottom half of the dropdown container transitions its height.</li>
<li>As the dropdown container moves, the previous contents fade out and slightly to the opposite direction, as if the dropdown is leaving them behind, while the new contents slide into view.</li>
</ol>
<p>There are a few useful guiding rules to keep in mind as we embark on reproducing this animation in React. Where possible, let’s keep things simple by having the browser manage layout. We’ll do this by keeping elements in the regular <abbr title="Document Object Model">DOM</abbr> flow instead of using absolute positioning and manual calculation. Rather than having a single dropdown component that we have to relocate every time a user's mouse position changes, we’ll render a single dropdown in the appropriate navbar section.</p>
<p class="explanation">We’ll use the <a href="https://css-tricks.com/animating-layouts-with-the-flip-technique/">FLIP technique</a> to create the illusion that the three separate dropdown components are actually a single, moving component.</p>
<h3>Scaffolding out the UI with styled-components</h3>
<p>To start with, we'll build an unanimated navbar component that simply takes a configuration object of titles and dropdown components and renders a navbar menu. The component will show and hide the relevant dropdown on mouseover. </p>
<p>We’ll build the <abbr title="User Interface">UI</abbr> components using <a href="https://www.styled-components.com/">styled-components</a>. Not only are they a convenient way to build a modular UI, but they have <a href="https://www.styled-components.com/docs/basics#animations">a great API for adding configurable CSS keyframe animations</a>. It turns out CSS animations and React play really nicely together, so we’ll be using CSS keyframes to add many of the animations later on.</p>
<p>With the components assembled without any animations, we’ve created something that looks like this:</p>
<p data-height="620" data-theme-id="1" data-slug-hash="OELrzN" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu Before Animation" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/OELrzN/">React Stripe Menu Before Animation</a> by Alex (<a href="https://codepen.io/aholachek">@aholachek</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<p>Notice that the gray background at the bottom of the menu is missing. It’s the only element that we’re going to have to take out of the regular DOM flow and absolutely position, so we’ll ignore it for now.</p>
<h3>Animating our dropdown with the FLIP technique</h3>
<p>We’re going to be using the <a href=" https://github.com/aholachek/react-flip-toolkit">react-flip-toolkit</a> library to help us animate the dropdown’s size and position. This is a library I put together to make advanced and complex transitions easier and configurable.</p>
<p>It provides us with two components: a top-level <code>&lt;Flipper/&gt;</code> component, and a <code>&lt;Flipped/&gt;</code> component to wrap any children we want to animate.</p>
<p>First, let's set up the <code>Flipper</code> wrapper component in the render function of <a href="https://github.com/aholachek/react-stripe-menu/blob/master/src/AnimatedNavbar.js#L82">AnimatedNavbar</a>:</p>
<pre rel="JavaScript" data-line="3,9"><code class="language-javascript">// currentIndex is the index of the hovered dropdown
&lt;Flipper flipKey={currentIndex}&gt;
  &lt;Navbar&gt;
    {navbarConfig.map((n, index) =&gt; {
      // render navbar items here
    })}
  &lt;/Navbar&gt;
&lt;/Flipper&gt;  </code></pre>
<p>Next, in our <a href="https://github.com/aholachek/react-stripe-menu/blob/master/src/DropdownContainer/index.js#L66">DropdownContainer</a> component, we'll wrap elements that need to be animated in their own <code>Flipped</code> components, making sure to give them each a unique <code>flipdId</code> prop:</p>
<pre rel="JavaScript" data-line="3,5,6,10"><code class="language-javascript">&lt;DropdownRoot&gt;
  &lt;Flipped flipId="dropdown-caret"&gt;
    &lt;Caret /&gt;
  &lt;/Flipped&gt;
  &lt;Flipped flipId="dropdown"&gt;
    &lt;DropdownBackground&gt;
      {children}    
    &lt;/DropdownBackground&gt;
  &lt;/Flipped&gt;
&lt;/DropdownRoot&gt;</code></pre>
<p>We're animating the <code>&lt;Caret/&gt;</code> component and the <code>&lt;DropdownBackground/&gt;</code> component separately so that the <code>overflow:hidden</code> style on the <code>&lt;DropdownBackground/&gt;</code> component doesn't interfere with the rendering of the <code>&lt;Caret/&gt;</code> component.</p>
<p>Now we’ve got a  working FLIP animation, but there’s still one problem: the contents of the dropdown appear weirdly stretched over the course of the animation:</p>
<p data-height="750" data-theme-id="1" data-slug-hash="MXgZxG" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu -- Error #1: no scale adjustment" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/MXgZxG/">React Stripe Menu -- Error #1: no scale adjustment</a> by Alex (<a href="https://codepen.io/aholachek">@aholachek</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<p>This unwanted effect occurs because scale transforms apply to children. If you apply a <code>scaleY(2)</code> to a div with some text inside, you will be scaling up the text and distorting it as well.</p>
<p>We can solve this problem by wrapping the children in a <code>Flipped</code> component with an <code>inverseFlipId</code> that references the parent component's <code>flipId</code> (in this case <code>"dropdown"</code>) to request that parent transforms be cancelled out for children. Because we still want translate transforms to affect the children, we also pass the <code>scale</code> prop to limit the cancellation to just scale changes.</p>
<pre rel="JavaScript" data-line="8"><code class="language-javascript">&lt;DropdownRoot&gt;
  &lt;Flipped flipId="dropdown-caret"&gt;
    &lt;Caret /&gt;
  &lt;/Flipped&gt;
  &lt;Flipped flipId="dropdown"&gt;
    &lt;DropdownBackground&gt;
      &lt;Flipped inverseFlipId="dropdown" scale&gt;
        {children}
      &lt;/Flipped&gt;
    &lt;/DropdownBackground&gt;
  &lt;/Flipped&gt;
&lt;/DropdownRoot&gt;</code></pre>
<p>Whew. All that work and we’ve created something that looks like this:</p>
<p data-height="750" data-theme-id="1" data-slug-hash="NzPpwG" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu -- Simple FLIP" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/NzPpwG/">React Stripe Menu -- Simple FLIP</a> by Alex (<a href="https://codepen.io/aholachek">@aholachek</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<h3>It’s all in the details</h3>
<p>It’s getting closer, but we still have to attend to the small details the make the animation look great: the subtle rotation animation when the dropdown appears and disappears, the cross-fade of previous and current dropdown children, and that silky-smooth gray background height animation.</p>
<h4>Configurable CSS keyframe animations with styled components</h4>
<p>Styled-components, which we've been using to build up the UI for this demo, offer a super convenient way to create <a href="https://www.styled-components.com/docs/basics#animations">configurable keyframe animations.</a> We'll use this functionality for both the dropdown enter animation and the cross-fade of the contents. We can pass in some basic information about the desired animation &mdash; whether the contents are animating in or out, and the direction the user's mouse has moved &mdash; and automatically get the appropriate animation applied. Here, for example, is the code for the crossfade animation in the <code>&lt;FadeContents&gt;</code> component: </p>
<pre rel="JavaScript"><code class="language-javascript">const getFadeContainerKeyFrame = ({ animatingOut, direction }) =&gt; {
  if (!direction) return;
  return keyframes`
  from {
    transform: translateX(${
      animatingOut ? 0 : direction === "left" ? 20 : -20
    }px);
  }
  to {
    transform: translateX(${
      !animatingOut ? 0 : direction === "left" ? -20 : 20
    }px);
    opacity: ${animatingOut ? 0 : 1};
  }
`;
};

const FadeContainer = styled.div`
  animation-name: ${getFadeContainerKeyFrame};
  animation-duration: ${props =&gt; props.duration * 0.5}ms;
  animation-fill-mode: forwards;
  position: ${props =&gt; (props.animatingOut ? "absolute" : "relative")};
  opacity: ${props =&gt; (props.direction &amp;&amp; !props.animatingOut ? 0 : 1)};
  animation-timing-function: linear;
  top: 0;
  left: 0;
`;</code></pre>
<p>Each time the user hovers a new item, we'll provide not only the current dropdown, but the previous dropdown as children to the <code>DropdownContainer</code> component, along with information about which direction the user's mouse has moved. The <code>DropdownContainer</code> component will then wrap both its children in a new component, <code>FadeContents</code>, that will use the keyframe animation code above to add the appropriate transition.</p>
<p>Here's a link to the <a href="https://github.com/aholachek/react-stripe-menu/blob/master/src/DropdownContainer/FadeContents.js">full code for the <code>FadeContents</code> component.</a> </p>
<p><a href="https://github.com/aholachek/react-stripe-menu/blob/master/src/DropdownContainer/Components.js#L3">The dropdown's enter/exit animation</a> will function very similarly.</p>
<h4>The final touch: A fluid background animation</h4>
<p>Finally, let’s add the gray background animation. To keep this animation crisp, we need to diverge from our previous strategy of keeping normal DOM nesting and letting the browser handle layout, and perform some manual positioning calculations instead. We'll also need to interact directly with the DOM. In short, it's going to get a little messy.</p>
<p>Here's a visual representation of our basic approach:</p>
<p data-height="700" data-theme-id="0" data-slug-hash="qKRWBe" data-default-tab="result" data-user="aholachek" data-embed-version="2" data-pen-title="React Stripe Menu -- Animated Background" class="codepen">See the Pen <a href="https://codepen.io/aholachek/pen/qKRWBe/">React Stripe Menu -- Animated Background</a> by Alex (<a href="https://codepen.io/aholachek">@aholachek</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<p><script async src="https://static.codepen.io/assets/embed/ei.js"></script></p>
<p>We’ll absolutely position a gray div at the top of the <code>DropdownContainer</code>. In the <code>componentDidMount</code> lifecycle function of <code>DropdownContainer</code>, we’ll update the <code>translateY</code> transform of the gray background. If the dropdown container component only has a single child (which means the user has only hovered a single dropdown so far), we’ll set the gray div’s <code>translateY</code> to the height of the first dropdown section. If there are two children, including a previous dropdown, we’ll instead set the initial <code>translateY</code> to the height of the previous dropdown’s first section, and then animate the <code>translateY</code> to the height of the current dropdown’s first section. Here's the function the gets called in <code>componentDidMount</code>:</p>
<pre rel="JavaScript"><code class="language-javascript">const updateAltBackground = ({
  altBackground,
  prevDropdown,
  currentDropdown
}) =&gt; {
  const prevHeight = getFirstDropdownSectionHeight(prevDropdown)
  const currentHeight = getFirstDropdownSectionHeight(currentDropdown)
  
  // we'll use this function when we want a change 
  // to happen immediately, without CSS transitions
  const immediateSetTranslateY = (el, translateY) =&gt; {
    el.style.transform = `translateY(${translateY}px)`
    el.style.transition = "transform 0s"
    requestAnimationFrame(() =&gt; (el.style.transitionDuration = ""))
  }

  if (prevHeight) {
    // transition the grey ("alt") background from its previous height
    // to its current height
    immediateSetTranslateY(altBackground, prevHeight)
    requestAnimationFrame(() =&gt; {
      altBackground.style.transform = `translateY(${currentHeight}px)`
    })
  } else {
    // immediately set the background to the appropriate height
    // since we don't have a stored value
    immediateSetTranslateY(altBackground, currentHeight)
  }
}</code></pre>
<p>This approach requires <code>DropdownContainer</code> to use a <code>ref</code> and reach inside its children to take DOM measurements in the <code>getFirstDropdownSectionHeight</code> function, which feels sloppy. If you have any ideas for alternative implementations, please let me know in the comments!</p>
<h3>Wrapping up</h3>
<p>Hopefully this article has helped clarify some techniques you can use the next time you build an animation in React. There are normally multiple ways of achieving any effect, but often it makes sense to start with the simplest possible implementation &mdash; basic  components with some CSS transitions or keyframe animations &mdash; and scale up the complexity from there when necessary. In our case, that meant including an additional library, react-flip-toolkit, so we didn't have to worry about manually transitioning the position of the dropdown component across the screen. To fully recreate the animation, we did have to write a fair amount of code. But by breaking down this animation into separate parts and tackling them one-by-one, we managed to replicate a pretty cool UI effect in React.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/building-a-complex-ui-animation-in-react-simply/">Building a Complex UI Animation in React, Simply</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/building-a-complex-ui-animation-in-react-simply/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">272368</post-id>	</item>
		<item>
		<title>Fast, Good, Local Site Search with Jetpack</title>
		<link>https://css-tricks.com/fast-good-local-site-search-with-jetpack/</link>
		<comments>https://css-tricks.com/fast-good-local-site-search-with-jetpack/#comments</comments>
		<pubDate>Thu, 12 Jul 2018 13:43:15 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Sponsored]]></category>
		<category><![CDATA[jetpack]]></category>
		<category><![CDATA[search]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273117</guid>
		<description><![CDATA[<p>If you have, say, 20 posts/pages on your WordPress site, the search functionality that is baked right into your self-hosted WordPress site will probably do a great job. Search is a pretty cool feature to ship with WordPress, truth be told. But as a site grows, you'll find limits. How it works behind the scenes is a touch over my head, but as I understand it, it does a <code>MySQL LIKE</code> query on your post titles and post content. <a href="https://core.trac.wordpress.org/ticket/7394#comment:72">It </a>&#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/fast-good-local-site-search-with-jetpack/">Fast, Good, Local Site Search with Jetpack</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>If you have, say, 20 posts/pages on your WordPress site, the search functionality that is baked right into your self-hosted WordPress site will probably do a great job. Search is a pretty cool feature to ship with WordPress, truth be told. But as a site grows, you'll find limits. How it works behind the scenes is a touch over my head, but as I understand it, it does a <code>MySQL LIKE</code> query on your post titles and post content. <a href="https://core.trac.wordpress.org/ticket/7394#comment:72">It tries its best</a> to order by relevance (like if it finds an exact sentence match), then largely orders by date.</p>
<p>If search is super important to your site (here on CSS-Tricks, on-site search gets used <em>a ton</em>) you'll probably look for other options. </p>
<p><strong>TLDR:</strong> <a href="https://jetpack.com/features/design/elasticsearch-powered-search/?aff=8638">Jetpack Search</a> is quite good out of the box, customizable, and requires very little effort.</p>
<p><span id="more-273117"></span></p>
<h3>A quick jaunt through a decade-long journey.</h3>
<p>I started with Google Site Search, which ultimately became Google Custom Search Engine. No bones about it, Google does incredible search results, and you can focus that lens directly on your own site. There is a catch here though. You're either going to have a bunch of ads in your search results, or you'll have to use the JSON API at a cost of $5 per 1,000 queries capped at 10k/day. </p>
<p>I'm all about paying a reasonable amount of money for quality search, so I tried Algolia for a while, as they have <a href="https://wordpress.org/plugins/search-by-algolia-instant-relevant-results/">a WordPress plugin</a>. I found it quite nice, but I couldn't quite get the search results as good as I liked. Nothing against Algoia here, it's quite an amazing product (we use it on CodePen for some stuff), but it wasn't a perfect fit for <em>this site's</em> blog search <em>for me</em>. </p>
<p>I considered trying out some similarly hardcore options like Amazon Cloudsearch (which also <a href="https://wordpress.org/plugins/cloud-search/">has a plugin</a>) or even spinning up a Solr instance (<a href="https://wordpress.org/plugins/solr-power/">same</a>). But I was nervous about the technical debt. I've learned over the years that the solutions that I like and will last are the ones with the least maintenance and least work to get going.</p>
<h3>I was pretty stoked when...</h3>
<p><a href="https://jetpack.com/features/design/elasticsearch-powered-search/?aff=8638">Jetpack rolled out search</a>, powered by <a href="https://www.elastic.co/products/elasticsearch">Elasticsearch</a>.  Because...</p>
<ul>
<li>It's a feature of Jetpack, which I already have installed and working.</li>
<li>It's extremely little work to use. I pretty much just turned it on.</li>
<li>It has good search results right out of the box.</li>
<li>The little work there was included:
<ul>
<li>Dropping in the widget.</li>
<li><a href="https://jetpack.com/support/search/customize-search/">Tweaking the algorithm</a> to provide even better results.</li>
</ul>
</li>
<li>There is no load on my server.</li>
<li>I have total control over the design. It's <a href="https://codex.wordpress.org/Creating_a_Search_Page">a WordPress theme <code>search.php</code> page</a> like any other.</li>
</ul>
<h3>Plus, filters!</h3>
<p>Another killer feature here is being able to offer filters on the original search to users. This uses any taxonomy, most likely the built-in categories and tags that many sites already have:</p>
<figure id="post-273674" class="align-none media-273674"><img src="https://css-tricks.com/wp-content/uploads/2018/07/jetpack-elasticsearch-powered-search.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_1000,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 1000w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_927,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 927w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_861,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 861w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_776,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 776w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_697,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 697w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_603,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 603w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_497,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 497w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_368,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 368w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530817230/jetpack-elasticsearch-powered-search_rfrrac.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /></figure>
<p>Here on CSS-Tricks, I could, say, search for "grid" then further refine it to content that we've specifically tagged "css grid."</p>
<figure id="post-273675" class="align-none media-273675"><img src="https://css-tricks.com/wp-content/uploads/2018/07/css-grid-results.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_1000,f_auto,q_auto/v1530817509/css-grid-results_pzj4x7.png 1000w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_851,f_auto,q_auto/v1530817509/css-grid-results_pzj4x7.png 851w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_680,f_auto,q_auto/v1530817509/css-grid-results_pzj4x7.png 680w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_475,f_auto,q_auto/v1530817509/css-grid-results_pzj4x7.png 475w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530817509/css-grid-results_pzj4x7.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /></figure>
<h3>WP Tavern <a href="https://wptavern.com/jetpack-5-4-introduces-beta-version-of-new-search-module-powered-by-elasticsearch-for-professional-plan-users">said it well</a></h3>
<blockquote><p>The Jetpack Professional plan’s $299/year price point is highly competitive for access to a hosted Elasticsearch engine. Ordinarily, developers looking for the most economical way to implement Elasticsearch on a WordPress site will have to host and manage their own instances on Amazon AWS or other cloud services. This often comes with more ongoing maintenance and setup.</p></blockquote>
<h3>What I like most about powering my search this way is that it feels like <em>The WordPress Way</em>.</h3>
<p>It feels like it will be supported forever. Plus, I'll have a support network for any issues and it will improve over time. There is little technical debt here, and in fact, I've reduced technical debt because of it. It's similar to what it felt like when I made a similar decision to use <a href="https://woocommerce.com/?aff=8638">WooCommerce</a> to sell things, bringing eCommerce under the WordPress roof, or when I moved our forums from other software to <a href="https://bbpress.org/">bbPress</a>.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/fast-good-local-site-search-with-jetpack/">Fast, Good, Local Site Search with Jetpack</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/fast-good-local-site-search-with-jetpack/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273117</post-id>	</item>
		<item>
		<title>Unused</title>
		<link>https://css-tricks.com/unused/</link>
		<comments>https://css-tricks.com/unused/#comments</comments>
		<pubDate>Wed, 11 Jul 2018 19:11:16 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[unused css]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273726</guid>
		<description><![CDATA[<p>I recently wrote <a href="https://css-tricks.com/heres-the-thing-about-unused-css-tools/"><em>Here’s the thing about "unused CSS" tools</em></a>, where I tried to enumerate all the challenges any tool would have in finding truly "unused" CSS. The overarching idea is that CSS selectors match elements in the DOM, and those elements in the DOM come from all sorts of places: your static templates, dynamic templates based on server-side state, and of course, JavaScript, which can manipulate the DOM in any way at all, including pull things from APIs &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/unused/">Unused</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>I recently wrote <a href="https://css-tricks.com/heres-the-thing-about-unused-css-tools/"><em>Here’s the thing about "unused CSS" tools</em></a>, where I tried to enumerate all the challenges any tool would have in finding truly "unused" CSS. The overarching idea is that CSS selectors match elements in the DOM, and those elements in the DOM come from all sorts of places: your static templates, dynamic templates based on server-side state, and of course, JavaScript, which can manipulate the DOM in any way at all, including pull things from APIs and third parties. </p>
<p><span id="more-273726"></span></p>
<p>With all that at play, how can any tool give us a truly accurate picture of unused CSS, to the point that actually removing that CSS isn't just as dangerous as leaving it alone?</p>
<p>So, I said:</p>
<blockquote><p>And yet, I don't think these tools are useless — they are just...<em>tools</em>. Their use can actually be a positive step toward better code. Their use says <em>OK, I admit it, I'm a little afraid our CSS.</em> You could use this tool to get a broad picture of what your unused CSS might be, then combine that with your own knowledge of your CSS code base to make more informed decisions.</p></blockquote>
<p>I stand by that, and it's worth pointing out some successful use cases.</p>
<p>Sarah Dayan just wrote <a href="https://medium.freecodecamp.org/how-i-dropped-250kb-of-dead-css-weight-with-purgecss-28821049fb"><em>How I dropped 250KB of dead CSS weight with PurgeCSS</em></a>. She was using <a href="https://tailwindcss.com/">Tailwind CSS</a>, an atomic CSS library that a lot of people have had success with. </p>
<blockquote><p>In my case, not only did I load the entire Tailwind CSS library, but I also added several variants to some modules. That ended up making the final minified CSS file weight 259 KB (before GZip).</p></blockquote>
<p>It's a tenth of that when gzipped, but still, that's a lot of CSS. Tailwind recommends using <a href="https://tailwindcss.com/docs/controlling-file-size/">PurgeCSS</a>, and that's what Sarah did. PurgeCSS doesn't handle any of things I mentioned, like state variations and JavaScript and whatnot, but it does look at any static files you'd like it to&mdash;both content and CSS&mdash;and do analysis from those. Even better, Sarah knew she had some third-party stuff going on, so handled that situation specifically:</p>
<blockquote><p>PurgeCSS can’t detect that I need to keep selectors such as <code>.ais-Highlight</code>, because the components that use it only show up in the DOM at runtime.</p></blockquote>
<p>So she split off some of that CSS and updated the configuration. Then...</p>
<blockquote><p>With this new configuration, my final CSS file has gone from 259 KB to…9 KB!</p></blockquote>
<p>I love it. Using the tool, being super aware of what is happening on your site, and making final choices for a combined win.</p>
<p>Sarah also mentions how this concept of unused CSS is related in spirit to the concept of unused JavaScript. In JavaScript, it's referred to as tree shaking, and <a href="https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking/">as Jeremy Wagner puts it</a>:</p>
<blockquote><p>Tree shaking is a form of dead code elimination.</p></blockquote>
<p>I trust the tooling for tree shaking much more implicitly. These are tools written in JavaScript to look at JavaScript to discover JavaScript that isn't used. It seems like the intersection of technology isn't as wide when we're talking about tree shaking. Still, there could be things like configured third parties that call public functions on your site, or event handlers directly on HTML elements. Those things seem a bit more rare to me these days, but that's just my own experience bias.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/unused/">Unused</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/unused/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273726</post-id>	</item>
		<item>
		<title>Emojis as Icons</title>
		<link>https://preethisam.com/2018/06/25/how-to-use-emojis-as-icons/</link>
		<comments>https://css-tricks.com/emojis-as-icons/#respond</comments>
		<pubDate>Wed, 11 Jul 2018 19:07:51 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Link]]></category>
		<category><![CDATA[emoji]]></category>
		<category><![CDATA[icons]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273843</guid>
		<description><![CDATA[<p>There are lots of unicode symbols that make pretty good icons already, like arrows (←), marks (✘), and objects (&#x2702;︎).You can already colorize these like a normal font glyph. Then, there are emojis, those full-color suckers we all know about. What if you could take just the <em>shape</em> of an emoji and use it like a normal glyph? You can! </p>
<p>Preethi Sam shows how:</p>
<p><code class="language-css">.icon {<br />
  color: transparent;<br />
  text-shadow: 0 0 #ec2930;<br />
}</code></p>
<p>Plus, an alternate technique using <code>background-clip</code>. &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/emojis-as-icons/">Emojis as Icons</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>There are lots of unicode symbols that make pretty good icons already, like arrows (←), marks (✘), and objects (&#x2702;︎).You can already colorize these like a normal font glyph. Then, there are emojis, those full-color suckers we all know about. What if you could take just the <em>shape</em> of an emoji and use it like a normal glyph? You can! </p>
<p>Preethi Sam shows how:</p>
<pre rel="CSS"><code class="language-css">.icon {
  color: transparent;
  text-shadow: 0 0 #ec2930;
}</code></pre>
<p>Plus, an alternate technique using <code>background-clip</code>. Note that emojis render differently across platforms, so careful!  </p>
<p><a href="https://preethisam.com/2018/06/25/how-to-use-emojis-as-icons/" title="Direct link to featured article">Direct Link to Article</a> &#8212; <a href="https://css-tricks.com/emojis-as-icons/">Permalink</a></p><p>The post <a rel="nofollow" href="https://css-tricks.com/emojis-as-icons/">Emojis as Icons</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/emojis-as-icons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273843</post-id>	</item>
		<item>
		<title>Hyperlinking Beyond the Web</title>
		<link>https://css-tricks.com/hyperlinking-beyond-the-web/</link>
		<comments>https://css-tricks.com/hyperlinking-beyond-the-web/#respond</comments>
		<pubDate>Wed, 11 Jul 2018 13:50:53 +0000</pubDate>
		<dc:creator><![CDATA[Atishay Jain]]></dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[links]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273403</guid>
		<description><![CDATA[<p>Hyperlinks are the oldest and the most popular feature of the web. The word <em>hypertext</em> (which is the <code>ht</code> in <code>http/s</code>) means text having hyperlinks. The ability to link to other people’s hypertext made the web, a web &#8212; a set of connected pages. This fundamental feature has made the web a very powerful platform and it is obvious that the world of apps needs this feature. All modern platforms support a way for apps to register a <abbr title="Uniform Resource Identifier">URI</abbr>&#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/hyperlinking-beyond-the-web/">Hyperlinking Beyond the Web</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Hyperlinks are the oldest and the most popular feature of the web. The word <em>hypertext</em> (which is the <code>ht</code> in <code>http/s</code>) means text having hyperlinks. The ability to link to other people’s hypertext made the web, a web &mdash; a set of connected pages. This fundamental feature has made the web a very powerful platform and it is obvious that the world of apps needs this feature. All modern platforms support a way for apps to register a <abbr title="Uniform Resource Identifier">URI</abbr> (custom protocol) and also have universal links (handling web links in an app). </p>
<p>Let’s see why we’d want to take advantage of this feature and how to do it.</p>
<p><span id="more-273403"></span></p>
<h3>Why have app links at all?</h3>
<p>Creating URIs that apps can open provides a unique set of benefits. A <abbr title="Uniform Resource Locator">URL</abbr> encapsulates the entire state of the webpage (it used to before the advent of Single Page Applications (SPAs) heavy with JavaScript and even in them, it is best practice to maintain this functionality) or a web application and it is advisable to have routing in applications so that a URL navigation is never broken. URLs have allowed browsers to have consistent back and forward buttons and reload and bookmark pages. And things like Google’s search depend on webpage addresses and web applications to navigate users to the correct place. Allowing applications to open URLs provides a way to link content and functionality in your application from other applications, websites and even internally within the application, like help/tutorial pages.</p>
<p class="explanation"><strong>Trivia!</strong> Desktop applications have had hyperlinks even before the world wide web like Hypercard on the Mac in 1987 and Windows Help in 1990. The revolutionary thing about hyperlinks in the web is that you can link content that was created and is owned by others. This concept of cross app linking was not popular in traditional platforms until the rise of smartphones.</p>
<h4>App links over the command line</h4>
<p>There is already a shell for the command line interface and it supports passing arguments, redirecting the results from one application to anther, and even scripting. Still, the need for having support for hyperlinks is desirable for various reasons:</p>
<ul>
<li><strong>Security:</strong> The command line script is too complicated to get right. It is very risky to expect users of technology (who are not developers) to understand how to sanitize a command line script and make sure that the script does not accidentally call something, like  <code>rm -rf ~/Documents</code>. The command line is not sand-boxed and though it provides a power, it puts responsibility on users and is prone to exploits.</li>
<li><strong>Browser support:</strong> The browser cannot provide the command line interface. It is built to run untrusted third party code in a sandbox and that cannot be broken. And, if we don’t respect the rules of web technology, the app would silo itself out of the Internet. That is too much to lose.</li>
<li><strong>Mobile:</strong> In the world of mobile, a touch keyboard is not as good and intuitive as the physical ones used on the desktop. Therefore, even if command line was present, it would be very difficult to use. All the power of redirection and chaining is not as effective as it is over a keyboard-based command line. A simpler solution like URL is more desirable in this context.</li>
<li><strong>State:</strong> Theoretically, the command line interface can provide a way to have the application available in any state. But, practically, it was built for a separate purpose: to launch the application to perform a task and then return with a code. Additionally, it was de-prioritized by the <abbr title-"Graphical User Interfaces">GUIs</abbr> in Windows and Mac. Many applications (like Microsoft Word and Adobe Photoshop) never had full-fledged command line support. They are not even present in the environment PATH and need the full path to be launched. URIs provide a fresh way to look at connecting information as well as functionality between applications. They are a secure, modern way of inter-app communication where the user does not have to think.</li>
</ul>
<h3>Linking Terminology</h3>
<h4>Deep Link</h4>
<p>Deep link is the concept where a specific page/functionality within the website/application can be linked to. For example, <code>https://css-tricks.com/link/to/this/page</code> is a deep link for this page within the broader <code>https://css-tricks.com</code> link. Unlike superficial links that open applications, deep links open the application in a specific state, with passed data, ready to be consumed. Custom URI (described below) were the first ways to achieve deep linking within the app and "deep linking" in many contexts is now synonymous with custom URI, though it can also mean Universal links.</p>
<h5>Custom URI</h5>
<p>The web always had certain custom URIs that linked to default applications, like <code>mailto:username@host.com</code> and <code>tel:1234567890</code> for email and telephone, respectively. It was an obvious choice to extend this and all major platforms provide a way for an app to register a URI protocol &mdash; like <code>css-tricks://&lt;link details&gt;</code> &mdash; that can open a deep linked native application. It should encapsulate the entire state and provide all routing benefits to open the app if it is available on the system. These provide a good interface for inter-app communication when the application is already installed and the desire is to open the user’s preferred application (like the browser or email client) that implements the desired protocol.</p>
<p>Custom URIs are great for the class of applications where the user wants to have a choice to select an app to perform a certain choice, but they are not great for the task of linking the website to the native application due to the following reasons:</p>
<ul>
<li><strong>Installation:</strong> Unlike the web, native apps need installation and, if they are not installed, you have two options: send the user to the app store (which also provides a custom URI) or properly fall back. Both these tasks require additional code which the URI does not encapsulate. The app store URI would be another separate URI that we need to store. Additionally, we have to detect whether the app is installed, which requires navigating to the URI and handling the error in navigation akin to a <code>404</code> since the protocol is not registered. This is not as simple as an anchor tag (<code>&lt;a href="{URL}"&gt;&lt;/a&gt;</code>) and, therefore, is a frequent source of developer complaints.</li>
<li><strong>Lack of a central registry:</strong> Even if everything is done perfectly right, users can still be thrown into an application that they should not have been linked to. Unlike the DNS system (which guarantees uniqueness of domain names), application platforms do not have a central repository. So, multiple apps could register <code>csstricks</code> as a custom URI. Now, someone else could also register the same URI as one app and if the other application is installed on the system instead of the desired one, it could launch instead. The freedom of a custom URI for app selection works against the case where we always want to open a specific application.</li>
<li><strong>Third Party Linking:</strong> Giving a custom URI to third parties because of the issues we’ve covered above is cumbersome and verifying them is painful. It is also a new protocol. Every website has links to the web content and updating the entirety of the Internet is not possible. It may not even be desired for SEO purposes.</li>
</ul>
<p>Therefore, if we want to give the user the experience where the app is the preferred way to view content, custom URIs are not the perfect solution. This can be mitigated with smart app banners to some extent, where the banner would be displayed when the browser gets the custom URI and the app store link to identify the application. But this will not be seamless.</p>
<p class="explanation"><strong>Trivia!</strong> URI and URLs are slightly different by definition, although they are used interchangeably. URI stands for Uniform Resource Identifier which means that it encapsulates everything required to get a resource. URL (Uniform Resource Locator) is a special type of URI that identifies the resource on the web. So, technically, a web address is a URL but something like <code>csstricks://</code> would be just a URI.</p>
<p>These are all possible use cases where a custom URI might make sense:</p>
<ul>
<li>You need to provide a URI that many third party client apps can register. Say you have a <code>git://</code> in your app and the user’s favorite git client could register that URI and clone the repo when the the link is clicked.</li>
<li>You do not have a fully featured website.</li>
<li>You need a URI that is easy for users to remember.</li>
<li>You are willing to handle all edge cases related to navigation.</li>
</ul>
<p>You can use custom URIs in parallel to the Universal link we’ll cover next and they can benefit from exposing the custom URL for inter-app communication while leaving the Universal link for the special case of web-to-app navigation. Android officially calls custom URIs <em>deep links</em>.</p>
<figure id="post-273405" class="align-none media-273405"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-1.jpg" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_1000,f_auto,q_auto/v1530310996/hyperlink-1_ipb6ux.jpg 1000w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_745,f_auto,q_auto/v1530310996/hyperlink-1_ipb6ux.jpg 745w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530310996/hyperlink-1_ipb6ux.jpg 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Control flows for Custom URI, Universal Link and Instant Apps</figcaption></figure>
<h5>Universal link, App Link or App URI</h5>
<p>The solution to the problem of web-to-app redirection can be solved with the Universal link (as it’s called in Mac and iOS), App Link (as it’s called in Android) or App URI (as it’s called in Windows UWP), which are different names of the same concept. This encapsulates the logic that every website needs to write in order to detect installed apps, launch correct pages and handle navigation failures in the case of custom URLs. It is very similar to the smart app banner in the sense that you need to provide an app store link to the application on your website for verification but it removes all the redundancy. Since the existing http(s) URL already contains the entire state, registering the custom URI is redundant. The original URL could itself navigate to the app with the website providing a fallback experience in case the application is not installed.</p>
<p>Once you register your app with a universal link, when the app is installed, the OS goes to the Internet to figure out the set of links that the app supports. Whenever any of those links get clicked, the native app gets launched instead of the browser. Full addressing support is now available in the application where a more customized experience can be provided falling back to the browser if the application is not installed. On important distinction with universal links is that they do affect regular browsing and therefore the OS providers keep then under tight lock and key.</p>
<p>A few good use cases for Universal links include:</p>
<ul>
<li>You have a fully featured website for a fallback.</li>
<li>The desired flow for users is from the website to the app.</li>
<li>You have already built up a lot of karma by having content from your website linked around the web.</li>
</ul>
<h4>Instant Link or Deferred Deep Link</h4>
<p>Deferred deep links provide the missing piece to the deep links if the user goes on to install the app when the link is opened. The user can be forwarded to the app store and the app store takes care of maintaining the context when the app is eventually launched after installation. This provides continuity in the case where an app is installed. Currently this method is only supported by Android (as Google Play Instant) and it is the option where you want to require users to move from the app to get the desired functionality. The hyperlink system on the web is seamless and clicking on a link gets you to the destination almost instantly (though a lot of things happen behind the scenes). Deferred deep links provide the same functionality for apps where clicking on a link can download the app and launch it with the correct page making all the tasks on app installation as seamless as possible.</p>
<p>You might consider using instant links if:</p>
<ul>
<li>You need the users to use the app and not the website, even if they come from the browser (except for rare cases where they are low on disk space or have turned this feature off to save bandwidth).</li>
<li>Your key pages are always up-to-date in your application.</li>
<li>OK, always use them. With the extra SEO advantages that Google throws in for instant apps, there is no reason not to enable "instant" apps for an app that has Universal links.</li>
</ul>
<p>Now that we have a summary of what each term means, let’s see how we can go about creating these on specific platforms.</p>
<h3>Setting up application hyperlinks</h3>
<h4>MacOS and iOS</h4>
<h5>Custom URI</h5>
<p>Registering a custom URI in MacOS and iOS is extremely easy. In Xcode, go to the <code>Info.plist</code> file of the project and select the option <code>URL Types</code>. Create an item of type <code>URL Schemes</code> inside the file where you can add all of the URL schemes you wish to support. </p>
<p class="explanation"><strong>Note:</strong> The default <code>URL Identifier</code> filled in by Xcode within URL Types is optional.</p>
<figure id="post-273407" class="align-none media-273407"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-2.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_950,f_auto,q_auto/v1530311015/hyperlink-2_zitvyc.png 950w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311015/hyperlink-2_zitvyc.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>URL Scheme in Xcode</figcaption></figure>
<pre rel="Info.plist"><code class="language-markup">&lt;key&gt;CFBundleURLTypes&lt;/key&gt;
&lt;array&gt;
  &lt;dict&gt;
    &lt;key&gt;CFBundleURLSchemes&lt;/key&gt;
    &lt;array&gt;
      &lt;string&gt;css-tricks&lt;/string&gt;
    &lt;/array&gt;
  &lt;/dict&gt;
&lt;/array&gt;</code></pre>
<p>For <strong>MacOS</strong>, you will receive calls to the <code>AppDelegate</code> where you can override the function:</p>
<pre rel="Swift"><code class="language-javascript">func application(_ application: NSApplication, open urls: [URL]) {
  // Select the first URL in the list of URL to open
  let url = urls[0]; 
  // Log the entire URL
  NSLog("%@", url.absoluteString)
}</code></pre>
<pre rel="Objective-C"><code class="language-javascript">-(void)application:(NSApplication *)application openURLs:(NSArray&lt;NSURL *&gt; *)urls {
  NSLog(@"%@", urls[0].absoluteString);
}</code></pre>
<p>In <strong>iOS,</strong> the function to receive the same call in <code>AppDelegate</code> is:</p>
<pre rel="Swift"><code class="language-javascript">func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -&gt; Bool {
  NSLog("%@", url.absoluteString)
  return true
}</code></pre>
<pre rel="Objective-C"><code class="language-javascript">-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options: (NSDictionary&lt;UIApplicationOpenURLOptionsKey,id&gt; *)options {
  NSLog(@"%@", url.absoluteString);
  return true;
}</code></pre>
<p>The <code>URL</code> type in all these cases contains the usual URL pieces like the path, the query and the search parameters.</p>
<h5>Universal Links</h5>
<p>Universal links are not supported in MacOS. To get universal links on iOS, you need to be registered with the Apple Developer Program or be an Apple Developer Enterprise Program member. Universal links on iOS are part of a wider concept of associated domains where the secrets of the website, like stored credentials, can be accessed from within the app and Apple does not allow regular Xcode users to dabble with universal links (if you accidentally enable the functionality for your App ID but do not put this in the entitlements, be prepared to get a validation error: ERROR ITMS-90046: "Invalid Code Signing Entitlements").</p>
<p class="explanation"><strong>Trivia!</strong> Universal links are a new concept and many of the Mac and Windows desktop applications are built with their own network stack, do not use the app store and may have been released years ago. Therefore, the OS does not have the control required to force all applications to follow the Universal link concept and open the app. Apple has chosen to not implement Universal Links in MacOS yet, while on Windows they only work in the Microsoft Edge browser and other UWP apps (which use the same technology).</p>
<p>To register for Universal links on iOS, you need to do the following things:</p>
<ol>
<li>Enable the App ID on the Apple Developer website to get the feature for associated domains.</li>
<li>Add the associated domains to the entitlements file.</li>
<li>Add a <code>apple-app-site-association</code> file to the web domain to provide ownership verification for the application.</li>
<li>Add code to handle clicks on Universal links.</li>
</ol>
<p>Steps 1 and 2 can be performed together in the Capabilities tab on Xcode if you are signed in and have the right profile selected (which is from an Apple Developer or Enterprise account). The Xcode UI provides an indication that the both items have been completed.</p>
<figure id="post-273408" class="align-none media-273408"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-3.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_950,f_auto,q_auto/v1530311032/hyperlink-3_j2loc8.png 950w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311032/hyperlink-3_j2loc8.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Associated Domains in the Capabilities section in Xcode</figcaption></figure>
<p>In the image above, Step 2 has been successfully completed, while Step 1 is pending. You can click on the exclamation mark to find the issue. It is important to prefix the domain with <code>applinks:</code> (that identifies that you need app links functionality; the same is used for shared credentials). This identifies that the App Link capability has been enabled. </p>
<p>The above steps can also be accomplished individually.</p>
<p>For Step 1, enable <code>Associated Domains</code> in the <code>App ID</code> section on the developer website (shown at the bottom of the image):</p>
<figure id="post-273409" class="align-none media-273409"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-4.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_850,f_auto,q_auto/v1530311047/hyperlink-4_mchizn.png 850w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311047/hyperlink-4_mchizn.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Apple Developer website to enable associated domains</figcaption></figure>
<p>For Step 2, add the following to the entitlements file:</p>
<figure id="post-273410" class="align-none media-273410"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-5.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_850,f_auto,q_auto/v1530311059/hyperlink-5_jasjjm.png 850w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311059/hyperlink-5_jasjjm.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Entitlements plist</figcaption></figure>
<pre rel="Entitlements File"><code class="language-markup">&lt;key&gt;com.apple.developer.associated-domains&lt;/key&gt;
&lt;array&gt;
  &lt;string&gt;applinks:css-tricks.com&lt;/string&gt;
&lt;/array&gt;</code></pre>
<p>Now, for Step 3, host a association file <code>apple-app-site-association</code> like <code>https://css-tricks.com/apple-app-site-association</code> with mime type <code>application/json</code> with the following JSON data from the root of your website:</p>
<pre rel="apple-app-site-association"><code class="language-javascript">{
  "applinks": {
    "apps": [],
    "details": {
      "ABCDEFGHIJ.com.ccs-tricks.mobile": {
        "paths": [
          "*"
        ]
      }
    }
  }
}</code></pre>
<p>The above snippet grants the app with ID <code>ABCDEFGHIJ.com.ccs-tricks.mobile</code> and provides access to all requests on the <code>https://css-tricks.com</code> domain. Note that this works with <code>https</code> but <code>http</code> is not supported. You can also use an alternative location like <code>https://css-tricks.com/.well-known/apple-app-site-association</code> for this. This URL is preferred as it can keep the Android, iOS and UWP association files in a safe separate folder that cannot be accidentally deleted.</p>
<p class="explanation"><strong>Trivia!</strong> Universal links do not guarantee that the app is opened. If the user goes back to the website from the header in iOS, the OS decides to default to the website from then on until the user decides to use the app by clicking on the header again from Safari.</p>
<p>For Step 4, add the following code to the app:</p>
<pre rel="Swift"><code class="language-javascript">func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -&gt; Void) -&gt; Bool {
  if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
    let url = userActivity.webpageURL!
    print(url.absoluteString)
  }
  return true
}</code></pre>
<pre rel="Objective-C"><code class="language-javascript">-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
  if(userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
    NSLog(@"%@", userActivity.webpageURL.absoluteString);
  }
  return true;
}</code></pre>
<p>Smart app banners on iOS are much simpler and can be added via a simple meta HTML tag in the head section like this:</p>
<pre rel="HTML"><code class="language-html">&lt;meta name="apple-itunes-app" content="app-id=123456789, app-argument=https://css-tricks.com/linkto/this/article, affiliate-data=optionalAffiliateData"&gt;</code></pre>
<p>They call the same method as Custom URIs we covered earlier.</p>
<h4>Windows (Traditional)</h4>
<p>On the traditional Windows platform (Win32/.NET), the custom URI (called <code>Custom Pluggable Protocol Handler</code>) is the only feature supported. Adding a custom URI in Windows involves adding a registry entry within <code>HKEY_CLASSES_ROOT</code>. The following will open the app with the absolute path given when clicking on a link to <code>css-tricks://&lt;text&gt;</code>. Due to using the command line, this always opens a new instance of the app and does not send the URL to the existing running application.</p>
<figure id="post-273411" class="align-none media-273411"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-6.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_850,f_auto,q_auto/v1530311077/hyperlink-6_jvyyfu.png 850w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311077/hyperlink-6_jvyyfu.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Registry Entry for providing application name</figcaption></figure>
<figure id="post-273412" class="align-none media-273412"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-7.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_850,f_auto,q_auto/v1530311091/hyperlink-7_rupvjq.png 850w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311091/hyperlink-7_rupvjq.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Registry entry for open command</figcaption></figure>
<pre rel=".reg"><code class="language-markup">Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\css-tricks]
"URL Protocol"=""
@="CSS Tricks (Application name)"

[HKEY_CLASSES_ROOT\css-tricks\shell]

[HKEY_CLASSES_ROOT\css-tricks\shell\open]

[HKEY_CLASSES_ROOT\css-tricks\shell\open\command]
@=""C:\\Windows\\notepad.exe" "%1""</code></pre>
<p>The above launches <code>notepad</code> with the command line argument corresponding to the URL supplied. Be aware that Notepad does not support custom URI and will not know what to do with the URL.</p>
<p>This behave is similar to passing this on the console and the application needs to make sure it properly distinguishes between the argument being a regular <abbr title="Command Line Interface">CLI</abbr> or a custom URI, as shown here:</p>
<pre rel="C#.NET"><code class="language-javascript">namespace ConsoleApplication1 {
  class Program {
    static void Main(string[] args) {
      if (args.Length &gt; 0 &amp;&amp; args[0].IndexOf("css-tricks:") == 0) {
        Console.Write(args[0]);
      }
    }
  }
}</code></pre>
<pre rel="C++"><code class="language-javascript">int main(int argc, char*argv[]) {
  if (argc &gt; 1) { // argv[0] is the file name.
    std::string word(argv[1]);
    if (word.compare(0, 11, "css-tricks:") == 0) {
      std::cout&lt;&lt;word;
    }
  }
  return 0;
}</code></pre>
<h4>Universal Windows Platform (UWP)</h4>
<p>On the Universal Windows Platform, you can use the package manifest to register both the custom URI and the Universal link (called <code>App URI Handler</code>).</p>
<h5>Custom URI</h5>
<p>Add a protocol declaration in the <code>package.appxmanifest</code>:</p>
<figure id="post-273413" class="align-none media-273413"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-8.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_1000,f_auto,q_auto/v1530311118/hyperlink-8_ylsrri.png 1000w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311118/hyperlink-8_ylsrri.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>Protocol Declaration in <code>package.appxmanifest</code></figcaption></figure>
<p>The same can be achieved in code:</p>
<pre rel="package.appxmanifest"><code class="language-markup">&lt;Extensions&gt;
  &lt;uap:Extension Category="windows.protocol"&gt;
    &lt;uap:Protocol Name="css-tricks" DesiredView="default"&gt;
      &lt;uap:DisplayName&gt;CSS Tricks&lt;/uap:DisplayName&gt;
    &lt;/uap:Protocol&gt;
  &lt;/uap:Extension&gt;
&lt;/Extensions&gt;</code></pre>
<p>...and this can now be handled within the app.</p>
<pre rel="C#"><code class="language-javascript">protected override void OnActivated(IActivatedEventArgs args) {
  base.OnActivated(args);
  if (args.Kind == ActivationKind.Protocol) {
    var e = args as ProtocolActivatedEventArgs;
    System.Diagnostics.Debug.WriteLine(e.Uri.AbsoluteUri);
  }
}</code></pre>
<pre rel="C++"><code class="language-javascript">// In App.xaml.h
virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e) override;

// In App.xaml.cpp
void App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ e) {
  if (e-&gt;Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol) {
    auto args = (ProtocolActivatedEventArgs^)e;
    auto url = args-&gt;Uri-&gt;AbsoluteUri;
  }
}</code></pre>
<h5>Universal Links (App URI)</h5>
<p>App URIs are only supported in the <code>Microsoft Edge</code> browser. They do not work in Internet Explorer, Chrome or Firefox. App URIs also have a package entry similar to the custom URI. It is not available in the UI of Visual Studio Code 2017. The <code>package.appxmanifest</code> entries are almost the same:</p>
<pre rel="package.appxmanifest"><code class="language-markup">&lt;Extensions&gt;
  &lt;uap3:Extension Category="windows.appUriHandler"&gt;
    &lt;uap3:AppUriHandler&gt;
      &lt;uap3:Host Name="css-tricks.com" /&gt;
    &lt;/uap3:AppUriHandler&gt;
  &lt;/uap3:Extension&gt;
&lt;/Extensions&gt;</code></pre>
<p>If <code>uap3</code> is not available, it can be added to the <code>Package</code> tag where <code>uap</code> is also defined:</p>
<pre><code>xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"</code></pre>
<p class="explanation">App URI is a relatively new feature of Windows and many UWP projects target older versions of windows as the minimum version. You might need to bump that up in order to support this feature.</p>
<p>Just like iOS, the website needs to verify the ownership of the domain for this to function. The can be done by hosting a file with mime type <code>application/json</code> at the root of your website, like <code>https://css-tricks.com/windows-app-web-link</code> or  <code>https://css-tricks.com/.well-known/windows-app-web-link</code> over <code>https</code> with the content:</p>
<pre rel="JSON"><code class="language-markup">{
  "packageFamilyName": "YourPackageFamilyNameHere",
  "paths": ["*"],
  "excludePaths": ["/none/*", "/robot.txt"]
}</code></pre>
<p>To get the package family name, run the following in  <code>Powershell</code> and search for your package path:</p>
<pre><code>Get-AppxPackage</code></pre>
<p>Handling App URIs requires the same code as custom URIs on Windows. By design, all you need to do is see the protocol field in the provided URI and write the corresponding logic.</p>
<p>Just like iOS, Windows users have a choice to disable opening apps. Windows provides registry settings to force apps to open (used for testing) and also a validator tool (located at <code>%windir%\system32\AppHostRegistrationVerifier.exe</code>) to verify if the above settings are correct.</p>
<h4>Android</h4>
<h5>Custom URI</h5>
<p>Android has supported custom URIs from the very beginning. This can be done with code. In <code>AndroidManifest.xml</code> add:</p>
<pre rel="AndroidManifest.xml"><code class="language-markup">&lt;activity android:name=".CustomUriActivity"&gt;
  &lt;intent-filter&gt;
    &lt;action android:name="android.intent.action.VIEW" /&gt;
    &lt;category android:name="android.intent.category.DEFAULT" /&gt;
    &lt;category android:name="android.intent.category.BROWSABLE" /&gt;
    &lt;data android:scheme="css-tricks" /&gt;
  &lt;/intent-filter&gt;
&lt;/activity&gt;</code></pre>
<p>The category <code>DEFAULT</code> is to ensure that there is no user action needed to enable it. <code>BROWSABLE</code> ensures that the custom URIs work in the browser.</p>
<p>This can then be handled in the <code>CustomUriActivity.java</code> on creation:</p>
<pre rel="CustomUriActivity.java"><code class="language-markup"">public class CustomUriActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
      Uri uri = intent.getData();
    }
  }
} </code></pre>
<h5>Universal Links</h5>
<p>Universal links in Android are very similar to App URIs. Android Studio provides a GUI tool to create this. Go to <code>Tools &gt; App Link Assistant</code>. This will provide the three tasks needed to create app links:</p>
<figure id="post-273419" class="align-none media-273419"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-9-a.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_846,f_auto,q_auto/v1530311596/hyperlink-9-a_rkinid.png 846w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_628,f_auto,q_auto/v1530311596/hyperlink-9-a_rkinid.png 628w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_447,f_auto,q_auto/v1530311596/hyperlink-9-a_rkinid.png 447w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311596/hyperlink-9-a_rkinid.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>App Link Assistant in Android</figcaption></figure>
<p>For Step 1, enter the URL mapping editor and click on the <code>+</code> button to open the URL mapping dialog:</p>
<figure id="post-273415" class="align-none media-273415"><img src="https://css-tricks.com/wp-content/uploads/2018/06/hyperlink-10.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_800,f_auto,q_auto/v1530311152/hyperlink-10_d7b4de.png 800w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1530311152/hyperlink-10_d7b4de.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
       (min-width: 1251px) calc( (100vw - 530px) / 2 )
       (min-width: 1086px) calc(100vw - 480px)
       (min-width: 626px)  calc(100vw - 335px)
                           calc(100vw - 30px)" alt="" /><figcaption>URL Mapping dialog</figcaption></figure>
<p>Once you click OK, it will show the code that could have been used alternatively in the <code>AndroidManifest.xml</code>:</p>
<pre rel="AndroidManifest.xml"><code class="language-markup">&lt;activity android:name=".CustomUriActivity"&gt;
  &lt;intent-filter&gt;
    &lt;action android:name="android.intent.action.VIEW" /&gt;
    &lt;category android:name="android.intent.category.DEFAULT" /&gt;
    &lt;category android:name="android.intent.category.BROWSABLE" /&gt;
    &lt;data android:scheme="http" android:host="css-tricks.com"/&gt;
  &lt;/intent-filter&gt;
&lt;/activity&gt;</code></pre>
<p class="explanation"><strong>Note:</strong> This exactly the same as as it is for a custom URI. In the Step 2, select the activity you assigned and it will add the code to load the action. The code is also exactly the same as was used for custom URIs above.</p>
<p>In the Step 3, it will generate the JSON file that needs to be added to the website as <code>application/json</code> mime at <code>https://css-tricks.com/.well-known/assetlinks.json</code>.</p>
<pre rel="JSON"><code class="language-markup">[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "css_tricks",
    "package_name": "com.css-tricks.android",
    "sha256_cert_fingerprints":
    ["17:CC:87:9C:C8:39:B1:89:48:E8:2E:9E:6F:FC:7D:03:69:4D:05:90:2A:84:B8:AE:5D:6B:30:97:F8:1C:2B:BF"]
  }
}]</code></pre>
<p>Android Studio automates the fingerprint generation that verifies the application identity. This identity is different for debug and the release version of the application.</p>
<h5>Instant Link</h5>
<p>Instant apps on Android require minor changes from the App links we covered earlier. To enable instant apps, modify the intent declared above to look like this:</p>
<pre rel="AndroidManifest.xml"><code class="language-markup">&lt;activity android:name=".CustomUriActivity"&gt;
  &lt;intent-filter android:autoVerify="true"&gt;
    &lt;action android:name="android.intent.action.VIEW" /&gt;
    &lt;category android:name="android.intent.category.DEFAULT" /&gt;
    &lt;category android:name="android.intent.category.BROWSABLE" /&gt;
    &lt;data android:scheme="http" android:host="css-tricks.com"/&gt;
    &lt;data android:scheme="https"/&gt;
  &lt;/intent-filter&gt;
&lt;/activity&gt;</code></pre>
<p>This allows both <code>http</code> and <code>https</code> URLs to be associated with the app, and the <code>autoVerify</code> tells the Android (Google Play) store to verify the manifest item automatically. Instant app is a Play Store/Google Chrome feature. The changes on the app are minimal to support this.</p>
<h4>Electron</h4>
<p>Electron apps are traditional MacOS and Windows apps with the chromium runtime. These platforms only support custom URIs that can be enabled without touching the compiled code. </p>
<p>For MacOS, edit <code>info.plist</code> as described in the MacOS section. The events are received by the app object as:</p>
<pre rel="info.plist"><code class="language-javascript">// In main.js
require('electron').app.on('open-url', (event, url) =&gt; {
  console.log(url);
});</code></pre>
<p>For Windows, add the registry entry as defined in the Windows section. The app will receive the custom URL in <code>process.argv</code>:</p>
<pre rel="info.plist"><code class="language-javascript">// In main.js
console.log(process.argv[2])</code></pre>
<p>In electron, you can load external web pages that can open URLs. If it is desired to handle only those URLs internally, that can be registered via an electron API:</p>
<pre rel="JavaScript"><code class="language-javascript">require('electron').protocol.registerHttpProtocol('css-tricks', (req, cb) =&gt; {
  console.log(req.url)
});</code></pre>
<h4>Web</h4>
<p>Going a complete circle, a website can also register custom URIs (like handling <code>mailto:</code> links via a website). Note that these only affect links provided in other websites and have nothing to do with links in other applications. The API is very restrictive and can be used only for a selected link type:  <code>web+&lt;insert anything here&gt;</code>. Browsers allow a pre-defined list of top level protocols to be registered:</p>
<ul>
<li><strong>Chrome</strong>: mailto, mms, nntp, rtsp, webcal</li>
<li><strong>Firefox</strong>: bitcoin, geo, im, irc, iris, magnet, mailto, mms, news, sip, sms, smite, ssh, tel, urn, webcal, wti, xmpp</li>
</ul>
<p>To register a protocol, you need to provide the same domain as the website that registers it (i.e. <code>https://www.css-tricks.com</code> can register only <code>https://www.css-tricks.com and not https://css-tricks.com</code>).</p>
<pre rel="JavaScript"><code class="language-javascript">if (navigator.registerProtocolHandler) {
  navigator.registerProtocolHandler("web+csstricks", "https://css-ticks.com/?s=%s", "CSS Tricks custom URI");
}</code></pre>
<p>The custom URIs are not indexed by search engines and therefore there is not much use to these apart from handling the links like <code>mailto</code>, <code>webcal</code> etc., that are already present in the web at multiple places.</p>
<h3>Wrapping Up</h3>
<p>Adding hyperlinks to native apps is an easy and effective way to seamlessly move the user across applications with graceful handling in case the application is not installed. With these links, applications can have provide paths all over the Internet which can be an effective promotional mechanism. Links also give websites a way to provide data and launch the applications on a specific page without the security worries that plague the downloadable files or command line scripts allowing effective tutorials and bug reports. Routing and URLs provide for the core features of the World Wide Web like navigation, the back button, bookmarks and search. Hyperlinks in apps bring these learning from the (almost) 30 years of world wide web to native applications.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/hyperlinking-beyond-the-web/">Hyperlinking Beyond the Web</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/hyperlinking-beyond-the-web/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273403</post-id>	</item>
		<item>
		<title>Random Day in the Life of a Developer</title>
		<link>https://css-tricks.com/random-day-in-the-life-of-a-developer/</link>
		<comments>https://css-tricks.com/random-day-in-the-life-of-a-developer/#comments</comments>
		<pubDate>Tue, 10 Jul 2018 22:20:55 +0000</pubDate>
		<dc:creator><![CDATA[Chris Coyier]]></dc:creator>
				<category><![CDATA[Article]]></category>

		<guid isPermaLink="false">https://css-tricks.com/?p=273605</guid>
		<description><![CDATA[<p>Yesterday, I started going through my email as soon as I got to work. I always start my day with email. <a href="http://email-is-good.com/">I kinda like email.</a> I read some interesting things in keeping up with the industry. I deal with some business things. I handle with some personal things. I make a note of the most important stuff going on. </p>
<p>Then I kept working!</p>
</p>
<hr />
<p>I fixed a bug that was affecting Firefox exclusively. This code would set some elements in a &#8230;</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/random-day-in-the-life-of-a-developer/">Random Day in the Life of a Developer</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Yesterday, I started going through my email as soon as I got to work. I always start my day with email. <a href="http://email-is-good.com/">I kinda like email.</a> I read some interesting things in keeping up with the industry. I deal with some business things. I handle with some personal things. I make a note of the most important stuff going on. </p>
<p>Then I kept working!</p>
<p><span id="more-273605"></span></p>
<hr>
<p>I fixed a bug that was affecting Firefox exclusively. This code would set some elements in a row with flexbox, let them wrap, then looked at all of them to see which ones wrapped and which ones didn't. Then it put the ones that wrapped in an overflow menu. Seemed simple enough. But some of the items were in <strong>bold</strong> text and some were not. The ones that were not bold were somehow pushed down 1.5px in Firefox (but in no other browser I looked at). The code checked to see if the <code>top</code> position was anything higher than <code>0</code> in order to check if they wrapped, and thus incorrectly marked some elements as being wrapped. The code had to be loosened up to accommodate that.</p>
<hr>
<p><a href="https://twitter.com/real_Css_Tricks">I tweeted some things</a> that looked particularly interesting that day.</p>
<hr>
<p>I had logged a bug a few days ago where a couple of small buttons were not styled or aligned how they should be in a common modal dialog. In diagnosing the issue, it looks like we had a weird git merge where some newer CSS was merged in that was assuming some HTML that wasn't quite live yet.</p>
<p>I decided the cleanest way forward was to make the small HTML change forthcoming in the new branch in a new fixer branch that could go out quickly. The alternative was reverting the CSS back and my guess was that would have caused worse merge conflicts later, plus it was worth taking advantage of the newer, nicer styling now.</p>
<p>Then, because of course it did, the small HTML change caused another bug. The JavaScript that was watching those buttons couldn't find them anymore and I failed to test it or, ideally, write a test for it. So, I had to fix that too, but sadly only after a user reported it. Gosh, I gotta improve our integration tests there.</p>
<hr>
<p>I was in two meetings. One was a standard weekly catchup meeting. One was with a new person I'd never met before and we were exploring if there was an interesting way we could work together.</p>
<hr>
<p>I fixed a bug with some bad backend logic (that I had written myself, of course). I wanted to display some information to a user based on, I thought, if they were on a team or not. I got that logic right, but it turns out the messaging was depended on that team being active (i.e. not expired in some way), so I had to update the logic to account for that. My team member helped me think it through because I didn't understand the way billing and active subscriptions were tied to the user well enough.</p>
<hr>
<p>I fixed a bug where a UI element was showing a strong warning state, but there was no way to find out what the problem was. It was a big red box with an exclamation point in it. It even had a hover state. But clicking it did nothing.</p>
<p>Turns out it was just a problem with event delegation. It was being added to the page after click handlers attempted to bind two elements like that. I fixed it by re-binding those click handlers after of the error UI was appended. It didn't feel great. A better fix might have been updating the event delegation to be more efficient about this case. But, I'd been burned by that in the past, so this fix seemed more robust, even if less elegant and efficient. </p>
<p>It made me <em>pine</em> for the page being ported to React, where a silly event delegation bug would have never been an issue. Not long after, I fixed another bug where a click handler had been bound to a <code>span.class_name</code>, and the span was updated to be a button. That was a semantic and accessibility win, but broke the handler.</p>
<p>Again, something that just doesn't happen in React.</p>
<hr>
<p>Then I wrote this blog post and went home.</p>
<p>The post <a rel="nofollow" href="https://css-tricks.com/random-day-in-the-life-of-a-developer/">Random Day in the Life of a Developer</a> appeared first on <a rel="nofollow" href="https://css-tricks.com">CSS-Tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://css-tricks.com/random-day-in-the-life-of-a-developer/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">273605</post-id>	</item>
	</channel>
</rss>
