<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/css" href="https://www.aaron-gustafson.com/c/feed.min.css" ?><feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:amg="https://www.aaron-gustafson.com.com/amg-dtd/"><title>Aaron Gustafson: Content tagged privacy</title><subtitle>The latest 20 posts and links tagged privacy.</subtitle><id>https://www.aaron-gustafson.com</id><link href="https://www.aaron-gustafson.com/feeds/privacy.xml" rel="self"/><link href="https://www.aaron-gustafson.com"/><author><name>Aaron Gustafson</name><uri>https://www.aaron-gustafson.com</uri></author><updated>2023-12-15T18:28:35Z</updated><entry><id>https://www.aaron-gustafson.com/notebook/sharing-in-the-age-of-3p-cookie-mageddon/</id><title type="html"><![CDATA[✍🏻 Sharing in the Age of 3p Cookie-mageddon]]></title><link href="https://www.aaron-gustafson.com/notebook/sharing-in-the-age-of-3p-cookie-mageddon/" rel="alternate" type="text/html" /><published>2023-12-15T18:28:35Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>Over a decade ago, I wrote up <a href="https://blog.easy-designs.net/archives/dont-sell-out-your-users/">detailed instructions on how to enable users to share your content on social media without allowing them to be tracked by every social media site via cookies</a>. In a few short weeks <a href="https://developers.google.com/privacy-sandbox/blog/cookie-countdown-2023oct">“third party” cookies will get the boot in Chromium-based browsers</a>. If you’re still relying on third party share widgets on your site, your users may start seeing problems. Now is a good time to replace them with code that Just Works™. Here’s how…</p><h2 id="sharing%2C-the-old-fashioned-way" tabindex="-1"><a class="header-anchor" href="#sharing%2C-the-old-fashioned-way" aria-hidden="true">#</a> Sharing, the Old-fashioned Way</h2><p>When it comes to sharing, there are myriad ways to do it. If you’re at all familiar with my work, it should come as no surprise that I always start with a universally-useable and accessible baseline and then <a href="/tags/progressive-enhancement">progressively enhance</a> things from there. Thankfully, every social media site I commonly use (with the exception of the Fediverse) makes this pretty easy by providing a form that accepts inbound content via the query string.<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup> You can <a href="https://www.linkedin.com/cws/share?url=https://www.aaron-gustafson.com/notebook/rebuilding-a-php-app-using-isomorphic-javascript-with-eleventy-and-netlify/">try LinkedIn’s to see it in action</a>.</p><p>Each service is a little different, but all function similarly. I support the following ones in this site:</p><table><caption>Social Media Sites and Their Sharing URLs</caption><thead><tr><th scope="col">Site</th><th scope="col">Destination</th><th scope="col">URL</th><th scope="col">Optional Params</th></tr></thead><tbody><tr><th scope="row">Twitter / X</th><td><a href="https://twitter.com/%EF%BF%BD4%EF%BF%BDintent/tweet%EF%BF%BD5%EF%BF%BD%EF%BF%BD6%EF%BF%BD%EF%BF%BD7%EF%BF%BDurl%EF%BF%BD8%EF%BF%BD%EF%BF%BD9%EF%BF%BD%EF%BF%BD10%EF%BF%BD%EF%BF%BD11%EF%BF%BD%EF%BF%BD12%EF%BF%BD">https://twitter.com/�4�intent/tweet�5��6��7�url�8��9��10��11��12�</a><tr><th scope="row">Hacker News</th><td><a href="https://news.ycombinator.com/%EF%BF%BD4%EF%BF%BDsubmitlink%EF%BF%BD5%EF%BF%BD%EF%BF%BD6%EF%BF%BD%EF%BF%BD7%EF%BF%BDu%EF%BF%BD8%EF%BF%BD%EF%BF%BD9%EF%BF%BD%EF%BF%BD10%EF%BF%BD%EF%BF%BD11%EF%BF%BDt%EF%BF%BD12%EF%BF%BD">https://news.ycombinator.com/�4�submitlink�5��6��7�u�8��9��10��11�t�12�</a> = the title you want to share </td></tr><tr><th scope="row">Facebook</th><td><a href="https://www.facebook.com/%EF%BF%BD4%EF%BF%BDsharer.php%EF%BF%BD5%EF%BF%BD%EF%BF%BD6%EF%BF%BD%EF%BF%BD7%EF%BF%BDu%EF%BF%BD8%EF%BF%BD%EF%BF%BD9%EF%BF%BD%EF%BF%BD10%EF%BF%BD%EF%BF%BD11%EF%BF%BD%EF%BF%BD12%EF%BF%BD">https://www.facebook.com/�4�sharer.php�5��6��7�u�8��9��10��11��12�</a><tr><th scope="row">LinkedIn</th><td><a href="https://www.linkedin.com/cws/share%EF%BF%BD4%EF%BF%BD%EF%BF%BD5%EF%BF%BD%EF%BF%BD6%EF%BF%BDurl%EF%BF%BD7%EF%BF%BD%EF%BF%BD8%EF%BF%BD%EF%BF%BD9%EF%BF%BD%EF%BF%BD10%EF%BF%BD%EF%BF%BD11%EF%BF%BD">https://www.linkedin.com/cws/share�4��5��6�url�7��8��9��10��11�</a><tr><th scope="row">Pinterest</th><td><a href="https://pinterest.com/%EF%BF%BD4%EF%BF%BDpin/create/button/%EF%BF%BD5%EF%BF%BD%EF%BF%BD6%EF%BF%BD%EF%BF%BD7%EF%BF%BDurl%EF%BF%BD8%EF%BF%BD%EF%BF%BD9%EF%BF%BD%EF%BF%BD10%EF%BF%BD%EF%BF%BD11%EF%BF%BDmedia%EF%BF%BD12%EF%BF%BD">https://pinterest.com/�4�pin/create/button/�5��6��7�url�8��9��10��11�media�12�</a> = an image to share<br><code>description</code> = the text you want to share</td></tr></tbody></table><p>Using this information, I created <a href="https://github.com/aarongustafson/aaron-gustafson.com/blob/main/src/_includes/partials/post/sharing.njk">a partial template for use on any page in this site</a> (though I mainly use it on blog posts right now). Each link includes useful text content (e.g., “Share on <strong>__</strong>”) and a local SVG of the service’s icon. Here’s a simplified overview of the markup I use:</p><pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>social-links social-links–share<span class="token punctuation">”</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>social-links__item<span class="token punctuation">”</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>{{ SHARE URL }}<span class="token punctuation">”</span></span><span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>social-link<span class="token punctuation">”</span></span><span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>nofollow<span class="token punctuation">”</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">&gt;</span></span>{{ SERVICE ICON }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>b</span><span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>social-link__text<span class="token punctuation">”</span></span><span class="token punctuation">&gt;</span></span>Share on {{ SERVICE NAME }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>b</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">&gt;</span></span></code></pre><p>You can check out the baseline experience on this very page by disabling JavaScript.</p><figure id="2023-12-14-01"><p><img src="https://www.aaron-gustafson.com/i/posts/2023-12-15/1.png" alt=""></p><figcaption>My baseline sharing component is a list of icon links.</figcaption></figure><p>It’s worth noting that I have chosen not to enforce opening these links in a new tab. You can do that if you like, but on mobile devices I’d prefer the user just navigate to the share page directly. You may have a different preference, but if you decide to spawn a new tab, be sure your link text lets folks know that’s what will happen. I do include a <a href="https://developer.mozilla.org/docs/Web/HTML/Attributes/rel#nofollow"><code>rel=&quot;nofollow&quot;</code></a> on the link, however, to prevent search spiders from indexing the share forms.</p><p>If you test out these links, you’ll notice many of the target forms will pick up a ton of information from your page automatically. By and large, this info is grabbed from your page’s <a href="https://developers.facebook.com/docs/opengraph/">Open Graph data</a> (<a href="https://developers.facebook.com/docs/sharing/webmasters#markup">stored in <code>meta</code> tags</a>) or <a href="https://www.w3.org/TR/json-ld11/">Linked Data</a> (as <a href="https://json-ld.org/">JSON-LD</a>). You can write that info to your page by hand or use a plugin to generate it for you automatically. There are a ton of options out there if you choose to go the later route (which I’d recommend).</p><h2 id="enhancement-level-1%3A-popup-share" tabindex="-1"><a class="header-anchor" href="#enhancement-level-1%3A-popup-share" aria-hidden="true">#</a> Enhancement Level 1: Popup Share</h2><p>If you played around with any of the various share forms, you probably noticed that they are, by and large, designed as discrete interactions best-suited to a narrow window (e.g., mobile) or popup. To provide that experience, I’ve long-relied on a little bit of JavaScript to launch them in a new, appropriately-sized window:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span><span class="token function">popup</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> $link <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">;</span><span class="token keyword">while</span><span class="token punctuation">(</span>$link<span class="token punctuation">.</span>nodeName<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">!=</span><span class="token string">“a”</span><span class="token punctuation">)</span><span class="token punctuation">{</span>$link <span class="token operator">=</span> $link<span class="token punctuation">.</span>parentNode<span class="token punctuation">;</span><span class="token punctuation">}</span>e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> popup <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>$link<span class="token punctuation">.</span>href<span class="token punctuation">,</span><span class="token string">“share”</span><span class="token punctuation">,</span><span class="token string">“height=500,width=600,status=no,toolbar=no,popup”</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">try</span><span class="token punctuation">{</span>popup<span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">catch</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> screen_width <span class="token operator">=</span><span class="token string">“visualViewport”</span><span class="token keyword">in</span> window<span class="token operator">?</span> window<span class="token punctuation">.</span>visualViewport<span class="token punctuation">.</span>width<span class="token operator">:</span> window<span class="token punctuation">.</span>innerWidth<span class="token punctuation">,</span>$links <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">“.social-links–share a”</span><span class="token punctuation">)</span><span class="token punctuation">,</span>count <span class="token operator">=</span> $links<span class="token punctuation">.</span>length<span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span>screen_width <span class="token operator">&gt;</span><span class="token number">600</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">while</span><span class="token punctuation">(</span>count<span class="token operator">–</span><span class="token punctuation">)</span><span class="token punctuation">{</span>$links<span class="token punctuation">[</span>count<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">“click”</span><span class="token punctuation">,</span> popup<span class="token punctuation">,</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>$links<span class="token punctuation">[</span>count<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">“.social-link__text”</span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerHTML <span class="token operator">+=</span><span class="token string">&quot; (in a popup)“</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>The first chunk defines a new function called <code>popup()</code> that will act as the event listener. It takes the event (<var>e</var>) as an argument and then finds the associated link (bubbling up through the DOM as necessary in that <code>while</code> loop). Once it finds the link, the function opens a new popup window (using <code>window.open()</code>). Then, to check if the popup was blocked, it attempts (within the <code>try…catch</code>) to focus it. If the focus succeeds, which means the popup wasn’t blocked, the script prevents the link from navigating the user to the <code>href</code> (which is the default behavior, hence <code>e.preventDefault()</code>).</p><p>The second block defines a couple of variables we’ll need. First, it captures the current <var>screen_width</var> using either the window’s <code>visualViewport</code> (if available) or its <code>innerWidth</code> (which is more old school). Next it grabs the social links (<var>$links</var>) and counts them for looping purposes (<var>count</var>).</p><p>The final block is a conditional that checks to see if the <var>screen_width</var> is wider than 600px (an arbitrary width that just feels right… your mileage may vary). If the screen is wider than that threshold, it loops through the links,<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup> adds the click handler, and adds some text to the link label to let folks know it will open a popup.</p><p>And with that, the first layer of enhancement is complete: Users with JavaScript support who also happen to be using a wider browser window will get the popup share form if the popup is allowed. If the popup isn’t allowed, they’ll default to the baseline experience.</p><h2 id="enhancement-level-2%3A-os-share" tabindex="-1"><a class="header-anchor" href="#enhancement-level-2%3A-os-share" aria-hidden="true">#</a> Enhancement Level 2: OS Share</h2><p>A few years back, browsers began participating in OS-level share activities. On one side, this allowed websites to share some data—URLs, text, files—to other apps on the device via <code>navigator.share()</code>. On the other side of the equation, Progressive Web Apps could advertise themselves—via the Manifest’s <code>share_target</code> member—as being able to receive content shared in this way.</p><p>Sharing a URL and text is <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share#browser_compatibility">really well supported</a>. That said, it’s only been around a few years at this point and some browsers require an additional permission to use the API.<sup class="footnote-ref"><a href="#fn3" id="fnref3">3</a></sup> For these reasons, it’s best to use the API as a progressive enhancement. Thankfully, it’s easy to test for support:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">“share”</span><span class="token keyword">in</span> navigator<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">// all good!</span><span class="token punctuation">}</span></code></pre><p>For my particular implementation, I’ve decided to swap out the individual links for a single button that, when clicked, will proffer the page’s details over to the OS’s share widget. Here’s the code I use to do that:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> $links <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">”.social-links–share&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>$parent <span class="token operator">=</span> $links<span class="token punctuation">.</span>parentNode<span class="token punctuation">,</span>$button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">“button”</span><span class="token punctuation">)</span><span class="token punctuation">,</span>title <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">“h1.p-name,title”</span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerText<span class="token punctuation">,</span>$description <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">‘meta[name=“og:description”],meta[name=“description”]’</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">,</span>text <span class="token operator">=</span> $description <span class="token operator">?</span> $description<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">“content”</span><span class="token punctuation">)</span><span class="token operator">:</span><span class="token string">“”</span><span class="token punctuation">,</span>url <span class="token operator">=</span> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href<span class="token punctuation">;</span>$button<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span><span class="token string">“Share &lt;svg&gt;…&lt;/svg&gt;”</span><span class="token punctuation">;</span>$button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">“click”</span><span class="token punctuation">,</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span>navigator<span class="token punctuation">.</span><span class="token function">share</span><span class="token punctuation">(</span><span class="token punctuation">{</span> title<span class="token punctuation">,</span> text<span class="token punctuation">,</span> url <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>$parent<span class="token punctuation">.</span><span class="token function">insertBefore</span><span class="token punctuation">(</span>$button<span class="token punctuation">,</span> $links<span class="token punctuation">)</span><span class="token punctuation">;</span>$links<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>The first block sets up my variables:</p><ul><li><var>$links</var> - A reference to the list (<code>ul</code>) of sharing links;</li><li><var>$parent</var> - the parent container of that list;</li><li><var>$button</var> - the button I’m going to swap in for the links;</li><li><var>title</var> - The page title (either from the page’s <code>h1</code> or <code>title</code> element);</li><li><var>$description</var> - A reference to a <code>meta</code> description element;</li><li><var>text</var> - The text content of that description, if one is found; and</li><li><var>url</var> - The URL to be shared.</li></ul><p>The second block sets up the button by inserting the text “Share” and an SVG share icon and setting an event listener on it that will pass the collected info to <code>navigator.share()</code>.</p><p>The third and final block swaps out the link list for the button.</p><h2 id="putting-it-all-together" tabindex="-1"><a class="header-anchor" href="#putting-it-all-together" aria-hidden="true">#</a> Putting It All Together</h2><p>The final step to putting this all together involves setting up the conditional that determines which enhancement is offered. To keep everything a bit cleaner, I’m also moving each of the enhancements into its own function:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token operator">!</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> navigator</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">function</span><span class="token function">prepForPopup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">// popup code</span><span class="token punctuation">}</span><span class="token keyword">function</span><span class="token function">popup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">// popup event handler</span><span class="token punctuation">}</span><span class="token keyword">function</span><span class="token function">swapForShareAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">// share button code</span><span class="token punctuation">}</span><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">“share”</span><span class="token keyword">in</span> navigator<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">swapForShareAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span><span class="token function">prepForPopup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span><span class="token keyword">this</span><span class="token punctuation">.</span>document<span class="token punctuation">,</span><span class="token keyword">this</span><span class="token punctuation">.</span>navigator<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>With this setup in place, I can provide the optimal experience in browsers that support the web share API and a pretty decent fallback experience to browsers that don’t. And if none of these enhancements can be applied, users can still share my content to the places I’ve identified… no cookies or third-party widgets required.</p><p>You can <a href="https://codepen.io/aarongustafson/pen/eYxajwy">see (and play with) an isolated demo of this interface over on Codepen</a>.</p><hr class="footnotes-sep"><section class="footnotes"><h4 class="hidden">Footnotes</h4><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>Interesting side-note: If you own a form like this on your site, <a href="/notebook/my-own-personal-pwa/">it makes a great share target</a>. <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p>Why a reverse <code>while</code> loop? Well, the order of execution doesn’t matter and decrementing <code>while</code> loops are faster in some instances. It’s a micro-optimization that boosts perf on older browsers and lower-end chipsets. <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p>Like many modern APIs, it also requires a secure connection (HTTPS). <a href="#fnref3" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content><amg:twitter><![CDATA[Are you still relying on third party share widgets? You should really stop that. Here’s how…]]></amg:twitter><amg:summary><![CDATA[Over a decade ago, I wrote up detailed instructions on how to enable users to share your content on social media without allowing them to be tracked by every social media site via cookies. In a few short weeks “third party” cookies will get the boot in Chromium-based browsers. If you’re still relying on third party share widgets on your site, now is a good time to replace them. Here’s how…]]></amg:summary><summary type="html"><![CDATA[<p>Over a decade ago, I wrote up detailed instructions on how to enable users to share your content on social media without allowing them to be tracked by every social media site via cookies. In a few short weeks “third party” cookies will get the boot in Chromium-based browsers. If you’re still relying on third party share widgets on your site, now is a good time to replace them. Here’s how…</p>]]></summary><category term="HTML" /><category term="privacy" /><category term="progressive enhancement" /><category term="the web" /><category term="user experience" /><category term="web development" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.aaron-gustafson.com/i/posts/2023-12-15/hero.jpg" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/links/in-2023-resolve-to-fix-your-organization-s-meta-pixel-problem-the-markup/</id><title type="html"><![CDATA[🔗 In 2023, Resolve to Fix Your Organization’s Meta Pixel Problem – The Markup]]></title><link href="https://www.aaron-gustafson.com/notebook/links/in-2023-resolve-to-fix-your-organization-s-meta-pixel-problem-the-markup/" rel="alternate" type="text/html" /><link href="https://themarkup.org/levelup/2023/01/31/in-2023-resolve-to-fix-your-organizations-meta-pixel-problem" rel="related" type="text/html" /><published>2023-02-03T17:52:25Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>It’s hard to believe it’s 11 years after <a href="https://blog.easy-designs.net/archives/dont-sell-out-your-users/">I wrote about dropping 3rd party share links &amp; such to protect your users’ privacy</a> and we’re still having to have this conversation. You need to get rid of the Facebook/Meta pixel. And if you can’t do it everywhere, at least do remove it from pages with sensitive content. Don’t sell out your users!</p>]]></content><amg:twitter><![CDATA[Don’t sell out your users. Drop the Facebook/Meta pixel, especially on pages with sensitive content.]]></amg:twitter><category term="privacy" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mrkp-static-production.themarkup.org/uploads/2023/01/pixel-audit-final-reduced-1200x628.jpg" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/spellcheckers-exfiltrating-pii_-not-so-fast/</id><title type="html"><![CDATA[✍🏻 Spellcheckers exfiltrating PII… not so fast]]></title><link href="https://www.aaron-gustafson.com/notebook/spellcheckers-exfiltrating-pii_-not-so-fast/" rel="alternate" type="text/html" /><published>2022-09-19T21:34:51Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>A recent post from the <a href="https://www.otto-js.com">Otto JS</a> research team highlighted <a href="https://www.otto-js.com/news/article/chrome-and-edge-enhanced-spellcheck-features-expose-pii-even-your-passwords">how spellcheck services can inadvertently exfiltrate sensitive user data, including passwords</a>, from your site. To be honest, I found the post a tad alarmist and lacking when it came to recommending solid protections. Consider this your no-nonsense guide to protecting your users’ sensitive information.</p><h2 id="background" tabindex="-1"><a class="header-anchor" href="#background" aria-hidden="true">#</a> Background</h2><p>In their research, the Otto JS team found that Chrome and Edge each use their own web services to drive their spellchecking services. In Chrome’s case it seems to only be with their “advanced spellcheck” feature turned on. I was able to validate this behavior using Charles on macOS Monterey using the latest Chrome Stable and the latest Edge Canary. I did not see the same exfiltration happen with Safari or Firefox. I created <a href="https://codepen.io/aarongustafson/pen/gOzWNgM">a CodePen form with several fields to test different permutations of form field attributes and behaviors</a>. What I am sharing, below, is a result of that testing.</p><h2 id="what-gets-sent%3F" tabindex="-1"><a class="header-anchor" href="#what-gets-sent%3F" aria-hidden="true">#</a> What gets sent?</h2><p>In both Chrome and Edge, the information sent to their services is the text value itself, disconnected from any specific field name. Chrome also sends the user’s language, and country. Edge, which uses the Microsoft Editor service under the hood, also sends a bunch of licensing details and the user’s language.</p><h2 id="password-fields-are-safe" tabindex="-1"><a class="header-anchor" href="#password-fields-are-safe" aria-hidden="true">#</a> Password fields are safe</h2><pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span><span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password<span class="token punctuation">”</span></span><span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password<span class="token punctuation">”</span></span><span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password<span class="token punctuation">”</span></span><span class="token punctuation">/&gt;</span></span></code></pre><p>Browsers do a lot to protect the contents of password fields already, so I wasn’t surprised to see that the contents of password fields are <strong>not</strong> passed to a spellcheck service.</p><p>If you are not using a true password field, however, the contents of that field are not protected in the same way. If you build a custom password control, you’re on the hook to replicate the entirety of the browser’s feature set when it comes to protecting user data. Unless you’re a glutton for punishment, you should probably just use the built-in password field instead.</p><h2 id="fields-marked-readonly-and-disabled-are-not-exposed" tabindex="-1"><a class="header-anchor" href="#fields-marked-readonly-and-disabled-are-not-exposed" aria-hidden="true">#</a> Fields marked readonly and disabled are not exposed</h2><p>As you’d hope, neither read-only fields nor disabled fields are exposed ot the service. This even holds true when you change the values of these fields programmatically or via DevTools.</p><p>I should note, however, that <code>readonly</code> fields <em>are</em> send to the server when the form is submitted and their contents are editable via JavaScript and DevTools, so you should always assume <code>readonly</code> fields are informational for the user only and never trust their contents on the server side. Fields that are marked <code>disabled</code>, in contrast, are never sent to the server.</p><h2 id="you-can-protect-interactive-fields-with-the-spellcheck-attribute" tabindex="-1"><a class="header-anchor" href="#you-can-protect-interactive-fields-with-the-spellcheck-attribute" aria-hidden="true">#</a> You can protect interactive fields with the <code>spellcheck</code> attribute</h2><pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span><span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>no-spellcheck<span class="token punctuation">”</span></span><span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>no-spellcheck<span class="token punctuation">”</span></span><span class="token attr-name">spellcheck</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>false<span class="token punctuation">”</span></span><span class="token punctuation">/&gt;</span></span></code></pre><p>The <code>spellcheck</code> attribute can be applied to any element and setting it to “false” instructs browsers to turn off spellchecking services for its contents. The post from Otto JS showed this being used globally on the <code>body</code> element, but that is overkill. It would be better to use the attribute on specific fields you want to protect, as shown above.</p><h2 id="don%E2%80%98t-forget-about-password-controls-that-support-show%2Fhide" tabindex="-1"><a class="header-anchor" href="#don%E2%80%98t-forget-about-password-controls-that-support-show%2Fhide" aria-hidden="true">#</a> Don‘t forget about password controls that support show/hide</h2><p>Edge has a neat feature in its password field implementation where it enables a user to toggle the visibility of the password within the control itself. When users show their password using that built-in functionality, no content is shared with the spellchecker. Not all browsers have this feature, however, which has led to JavaScript-based implementations that simply swap the “password” <code>type</code> value for “text” to show the contents and then swap it back again to hide the contents. Here’s a quick &amp; dirty example of what the toggle button’s event handler might look like:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span><span class="token function">togglePassword</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> $btn <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">,</span>$field <span class="token operator">=</span> $btn<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">“input”</span><span class="token punctuation">)</span><span class="token punctuation">,</span>state <span class="token operator">=</span> $field<span class="token punctuation">.</span>type<span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span>$btn <span class="token operator">&amp;&amp;</span> $field<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">if</span><span class="token punctuation">(</span>state <span class="token operator">==</span><span class="token string">“password”</span><span class="token punctuation">)</span><span class="token punctuation">{</span>$field<span class="token punctuation">.</span>type <span class="token operator">=</span><span class="token string">“text”</span><span class="token punctuation">;</span>$btn<span class="token punctuation">.</span>innerText <span class="token operator">=</span><span class="token string">“Hide”</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span>$field<span class="token punctuation">.</span>type <span class="token operator">=</span><span class="token string">“password”</span><span class="token punctuation">;</span>$btn<span class="token punctuation">.</span>innerText <span class="token operator">=</span><span class="token string">“Show”</span><span class="token punctuation">;</span><span class="token punctuation">}</span>e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function">stopPropagation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>The problem with this approach, when it comes to the spellchecker, is that the text field is considered fair game for checking. Its contents aren’t sent right away, but if the field receives focus or its value is changed in any way while in the “text” state, its contents are sent to the spellcheck service.</p><p>Protecting the field’s contents are fairly easy, however: turn off the spellchecker for that field, even in its “password” state.</p><pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span><span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password<span class="token punctuation">”</span></span><span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password-show-nospellcheck<span class="token punctuation">”</span></span><span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>password-show-nospellcheck<span class="token punctuation">”</span></span><span class="token attr-name">spellcheck</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">“</span>false<span class="token punctuation">”</span></span><span class="token punctuation">/&gt;</span></span></code></pre><p>With <code>spellcheck=&quot;false&quot;</code> in place, you can turn the field into a text field safely, without the contents being exposed to the spellcheck service.</p><hr><p>HTML is a pretty powerful language and, when wielded properly, can be an excellent tool for protecting user data, provided we use it properly. And know that you know how to protect your users’ sensitive data, minimize this tab and start filing those pull requests…</p>]]></content><amg:twitter><![CDATA[A collection of ways you can keep sensitive user data protected from accidental exfiltration.]]></amg:twitter><amg:summary><![CDATA[A recent post from the Otto JS research team highlighted how spellcheck services can inadvertently exfiltrate sensitive user data, including passwords, from your site. To be honest, I found the post a tad alarmist and lacking when it came to recommending solid protections. Consider this your no-nonsense guide to protecting your users’ sensitive information.]]></amg:summary><summary type="html"><![CDATA[<p>A recent post from the Otto JS research team highlighted how spellcheck services can inadvertently exfiltrate sensitive user data, including passwords, from your site. To be honest, I found the post a tad alarmist and lacking when it came to recommending solid protections. Consider this your no-nonsense guide to protecting your users’ sensitive information.</p>]]></summary><category term="browsers" /><category term="forms" /><category term="HTML" /><category term="security" /><category term="privacy" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.aaron-gustafson.com/i/posts/2022-09-19/hero.jpg" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/links/policymakers-sound-alarm-on-dark-patterns-deceptive-web-design-tricks/</id><title type="html"><![CDATA[🔗 Policymakers Sound Alarm On “Dark Patterns,” Deceptive Web Design Tricks]]></title><link href="https://www.aaron-gustafson.com/notebook/links/policymakers-sound-alarm-on-dark-patterns-deceptive-web-design-tricks/" rel="alternate" type="text/html" /><link href="https://www.ndtv.com/world-news/policymakers-sound-alarm-on-dark-patterns-deceptive-web-design-trick-2020659" rel="related" type="text/html" /><published>2019-04-10T15:18:25Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>The <a href="https://www.scribd.com/document/405606873/Detour-Act-Final">DETOUR Act</a>, introduced by Sens. Mark Warner and Deb Fischer, targets bad actors on the web. I need to read through it fully to get a sense of what covered and/or missing, but that this is happening is, I think, a good thing.</p>]]></content><category term="privacy" /><category term="user experience" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://c.ndtvimg.com/2019-04/4idbju0o_patterns650_625x300_10_April_19.jpg" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/links/a-crisis-of-permissions-80cf3b2c802e/</id><title type="html"><![CDATA[🔗 A Crisis of Permissions]]></title><link href="https://www.aaron-gustafson.com/notebook/links/a-crisis-of-permissions-80cf3b2c802e/" rel="alternate" type="text/html" /><link href="https://medium.com/samsung-internet-dev/a-crisis-of-permissions-80cf3b2c802e" rel="related" type="text/html" /><published>2018-10-03T22:26:05Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>More great thoughts on privacy and permissions from <a href="https://twitter.com/ThisIsJoFrank">Jo Franchetti</a>. I love her list of suggested improvements at the end. Especially “Encourage/standardise clearer wording and UI to explain reasons for permissions and how long permissions will be granted.”</p>]]></content><amg:twitter><![CDATA[More great thoughts on privacy and permissions from @ThisIsJoFrank]]></amg:twitter><category term="privacy" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://miro.medium.com/max/1200/1*k7jyt8okLpikXnuh09nq7A.png" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/links/thinking-about-permissions/</id><title type="html"><![CDATA[🔗 Thinking about permissions on the web]]></title><link href="https://www.aaron-gustafson.com/notebook/links/thinking-about-permissions/" rel="alternate" type="text/html" /><link href="https://sallylait.com/blog/2018/09/27/thinking-about-permissions/" rel="related" type="text/html" /><published>2018-10-03T22:16:30Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>I’ve been thinking quite a bit about privacy and permissions both generally and in web browsers and our product that rely on them. I very much appreciated this perspective from Sally Lait.</p><blockquote><p><em>[M]y</em> personal preference is generally to continue as normal, and then to use whatever’s being requested as an <strong>enhancement</strong>, as a natural part of whatever task I am hoping to do, at a time that makes sense to me rather than having it pushed on me out of context or at a time that doesn’t make sense.</p></blockquote><p>This is true for me as well. I hate going to a new site only to be immediately bombarded with requests to see my location or send me notifications. It makes your site appear desperate, socially-awkward, and a bit sociopathic.</p><p>I definitely believe there’s room for improvement in terms of how browsers relay requests for permission. Personally, I’d love to see permission requests require an accompanying link to the section of that site’s privacy policy covering how the information being requested will be used. Sure, most users probably won’t click on it, but having to provide something might make some developers think twice about it (and more directly tie these requests to the site’s governing entity from a legal standpoint).</p>]]></content><category term="privacy" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://sallylait.com/img/content/blog/20180927-hdr.jpg" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/links/collecting-user-data-while-protecting-user-privacy/</id><title type="html"><![CDATA[🔗 Collecting user data while protecting user privacy]]></title><link href="https://www.aaron-gustafson.com/notebook/links/collecting-user-data-while-protecting-user-privacy/" rel="alternate" type="text/html" /><link href="https://www.kryogenix.org/days/2018/02/20/collecting-user-data-while-protecting-user-privacy/" rel="related" type="text/html" /><published>2018-02-27T19:40:11Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<blockquote><p>What I want is a way that both sides can get what they want: companies and projects can be data-driven, and users don’t get their privacy compromised.</p></blockquote><p>Amen.</p>]]></content><amg:twitter><![CDATA[“What I want is a way that both sides can get what they want: companies and projects can be data-driven, and users don’t get their privacy compromised.”]]></amg:twitter><category term="web development" /><category term="privacy" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.kryogenix.org/days/2018/02/20/collecting-user-data-while-protecting-user-privacy/index.html.og_image.png" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/dont-sell-out-your-users/</id><title type="html"><![CDATA[✍🏻 Don’t Sell Out Your Users]]></title><link href="https://www.aaron-gustafson.com/notebook/dont-sell-out-your-users/" rel="alternate" type="text/html" /><published>2012-04-24T13:17:00Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>As a profession, we spend a lot of time thinking of the best ways to protect our users’ data and their privacy. In fact, most sites have exhaustive Privacy Policies detailing what information they collect and what they may do with it. That’s why I find it bizarre that many of these same sites have chosen to hand over their users’ browsing habits to third parties such as Twitter, Facebook, and Google without considering the implications.</p><p>You see, any time you add third party widget code—a “tweet this” button, for example—a request is made to that third party’s server in order to retrieve the required snippet of JavaScript. When that request is sent, information is sent to the server in the form of headers and (as is often the case) cookies. The <a href="http://en.wikipedia.org/wiki/List_of_HTTP_headers">headers usually contains general information about the browser being used, referring page, etc.</a> and don’t generally pose much of a threat, but <a href="http://en.wikipedia.org/wiki/Browser_cookies#Privacy_and_third-party_cookies">cookies are another matter altogether</a>.</p><p>When a user visits a site—say, Facebook—she is typically cookied by that site. Browsers give a user some modicum of control over what sites can set a cookie, so she could have opted out of receiving Facebook’s cookie, but most users don’t know enough to or don’t know how to opt-out of cookies. Once that cookie is in place, it is directly associated with a specific domain (e.g. <a href="http://facebook.com">facebook.com</a> or one of its hostnames). As long as that cookie is active, it remains on the users’ computer and accompanies any requests sent to the specifed domain. That means when this user visits a web page that includes a Facebook-supplied “Like” button, for example, Facebook can identify who she is (via the cookie) and what she’s looking at (via the request headers) without her even clicking the “Like” button. As she moves from page to page across the web, Facebook—or Google, or Twitter, or Pinterest, or AddThis, or any other service with decent distribution of its widgets—can effectively track her movement and build up a profile of her interests or browsing habits without her even knowing.</p><p>Now I’m pretty certain none of these companies started out wanting to track their users in such a manner, nor am I convinced many of them actively are (though I would not put it past Google or Facebook), but I think you can agree there is certainly potential for abuse here.</p><p>So what are we to do? Including buttons that easily allow our users to share content on their favorite social networks is extremely helpful for attracting more eyeballs to our content; it would be a shame to lose that opportunity. I agree, but just because a company offers a widget to make it simple for you to set up the button doesn’t mean you need to use it. Thankfully it isn’t really that difficult to host “share” buttons yourself, you just have to know how to do it.</p><p>Below is simplification of the current markup we use to achieve this in our blog. At present, we’ve chosen to support only four social networks: Twitter, Facebook, LinkedIn, and Google Plus.</p><code>html   &lt;section id=&quot;bookmark&quot;&gt;    &lt;h2&gt;Like it? Share it&lt;/h2&gt;    &lt;p class=&quot;twitter&quot;&gt;&lt;a href=&quot;https://twitter.com/intent/tweet?original_referer=THE-CURRENT-URL&amp;amp;source=tweetbutton&amp;amp;text=THE+TITLE+OF+THE+PAGE&amp;amp;url=THE-CURRENT-URL&amp;amp;via=OUR-TWITTER-ACCOUNT&quot;&gt;&lt;img src=&quot;/i/button-twitter.png&quot; alt=&quot;Tweet&quot;/&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p class=&quot;facebook&quot;&gt;&lt;a href=&quot;http://www.facebook.com/sharer.php?u=THE-CURRENT-URL&quot;&gt;&lt;img src=&quot;/i/button-facebook.png&quot; alt=&quot;Share on Facebook&quot;/&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p class=&quot;linkedin&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/cws/share?url=THE-CURRENT-URL&amp;amp;original_referer=THE-CURRENT-URL&quot;&gt;&lt;img src=&quot;/i/button-linkedin.png&quot; alt=&quot;Share on LinkedIn&quot;/&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p class=&quot;google_plus&quot;&gt;&lt;a href=&quot;https://plus.google.com/share?url=THE-CURRENT-URL&quot;&gt;&lt;img src=&quot;/i/button-googleplus.png&quot; alt=&quot;Share on Google Plus&quot;/&gt;&lt;/a&gt;&lt;/p&gt;   &lt;/section&gt; </code><p>To trigger a share via Twitter, we use Twitter’s “tweet intent” URL: <code><a href="https://twitter.com/intent/tweet">https://twitter.com/intent/tweet</a></code>. We then supply several key-value pairs as part of the query string:</p><ol><li>The referer (our page) as <code>original-referer</code>;</li><li>Any text we want included in the tweet—e.g. the title of the page—as <code>text</code> (n.b. be sure to replace spaces with “+”);</li><li>the page to share as <code>url</code>; and</li><li>(optionally) a Twitter account handle you’d like the tweet to appear “via”.</li></ol><p>Facebook seems less complicated, but it’s really not. Sure, the URL is simple—<code><a href="http://www.facebook.com/sharer.php">http://www.facebook.com/sharer.php</a></code> with the URL supplied as <code>u</code> in the query string—but to control what Facebook displays when your page is shared, you need to add some <code>meta</code> tags that describe the page as an <a href="http://ogp.me/">OpenGraph</a> object. Here’s a sample from this blog post:</p><code>html   &lt;meta property=&quot;og:site_name&quot; content=&quot;The Easy Designs Blog&quot;/&gt;   &lt;meta property=&quot;og:image&quot; content=&quot;/i/facebook-icon.png?v=20111226&quot;/&gt;   &lt;meta property=&quot;og:locale&quot; content=&quot;en_US&quot;/&gt;   &lt;meta property=&quot;fb:admins&quot; content=&quot;aaronmgustafson&quot;/&gt;   &lt;meta property=&quot;og:type&quot; content=&quot;article&quot;/&gt;   &lt;meta property=&quot;og:title&quot; content=&quot;Don’t Sell Out Your Users&quot;/&gt;   &lt;meta property=&quot;og:description&quot; content=&quot;Most sites have exhaustive Privacy Policies detailing what information they collect and what they may do with it, which is why I find it bizarre that many of these same sites have chosen to hand over their users’ browsing habits to third parties such as Twitter, Facebook, and Google without considering the implications.&quot;/&gt;   &lt;meta property=&quot;og:url&quot; content=&quot;http://blog.easy-designs.net/archives/dont-sell-out-your-users/&quot;/&gt; </code><p>This set of <code>meta</code> tags establishes this post as an “article” with a title, description, and a canonical URL, which ensures it is displayed in Facebook properly. Facebook maintains <a href="https://developers.facebook.com/docs/opengraph/">pretty decent documentation on OpenGraph and the pieces they support</a> and they also have a <a href="https://developers.facebook.com/tools/debug">handy testing tool you can use to see if everything is making it to them properly</a>. (It’s also worth noting that, using the debugger, you can also force Facebook to update any previously cached content from a given URL.)</p><p>Rounding out the pack are LinkedIn and Google Plus. Both use OpenGraph like Facebook does, but <a href="https://developer.linkedin.com/documents/setting-display-tags-shares">LinkedIn only supports a subset of OpenGraph tags</a>—<code class="html">og:title</code>, <code class="html">og:url</code>, and <code class="html">og:image</code> (though only if the image is wider than 150px and taller than 80px)—and <a href="https://developers.google.com/+/plugins/+1button/#plus-snippet">Google Plus would prefer you use the ridiculously convoluted attributes defined by schema.org</a>, but will fall back to OpenGraph or basic <code class="html">meta</code> title and description tags if necessary.</p><p>Now that the HTML links are working properly (you did test them, right?), you need some buttons. There are tons of options out there, but if you like the ones we are using, you can <a href="../../assets/posts/share-icons.psd">download a layered PSD from us</a> and tweak to your heart’s content. If, however, you want to go image-less, you could use one of the many great icon fonts out there (like <a href="http://keyamoon.com/icomoon/#toPreview">IcoMoon</a>). Whatever you do, just don’t link to images on a 3rd party site because then you are falling back into the same trap again because image requests also pass along headers and cookies.</p><p>And there you have it: a fully-funcitonal set of sharing tools that requires no JavaScript and doesn’t sacrifice your users’ privacy.</p><p>If you want to take it a step further, it’s pretty easy to add a tiny bit of JavaScript to make the links trigger a popup when there’s enough real estate (after all, you probably don’t want to do that on mobile). Here’s the jQuery code we currently use on this site for that purpose:</p><pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">‘#bookmark’</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">delegate</span><span class="token punctuation">(</span><span class="token string">‘a’</span><span class="token punctuation">,</span><span class="token string">‘click’</span><span class="token punctuation">,</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">&gt;</span><span class="token number">700</span><span class="token punctuation">)</span><span class="token punctuation">{</span>e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>window<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>href<span class="token punctuation">,</span><span class="token string">‘share-this’</span><span class="token punctuation">,</span><span class="token string">‘height=300,width=500,status=no,toolbar=no’</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>As you can see, with just a little bit of effort (and maybe a bit of research), it’s easy to protect your users. Please consider doing it on your own sites.</p>]]></content><amg:summary><![CDATA[As a profession, we spend a lot of time thinking of the best ways to protect our users’ data and their privacy. In fact, most sites have exhaustive Privacy Policies detailing what information they collect and what they may do with it…]]></amg:summary><summary type="html"><![CDATA[<p>As a profession, we spend a lot of time thinking of the best ways to protect our users’ data and their privacy. In fact, most sites have exhaustive Privacy Policies detailing what information they collect and what they may do with it…</p>]]></summary><category term="privacy" /><category term="user experience" /><category term="progressive enhancement" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/javascript-less-google-finally/</id><title type="html"><![CDATA[✍🏻 JavaScript-less Google+ (finally)]]></title><link href="https://www.aaron-gustafson.com/notebook/javascript-less-google-finally/" rel="alternate" type="text/html" /><published>2011-12-16T16:03:00Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>When we launched the mobile-first version of this blog, we opted <em>not</em> to include Google+ as one of the sharing options because there was no way to make it work without JavaScript (a fact which undermined both our progressive enhancement philosophy and the privacy of our readers). I tried digging into the (IMHO) over-engineered code that manages the +1 button to find an end point, but after about an hour of digging decided it wasn’t worth it. Thankfully there are others out there who are more persistent than I am and a way to share on Google+ without using the Google-supplied JavaScript is now available <a href="http://www.techlifeweb.com/2011/08/12/share-googleplus-cross-browser-bookmarklet/">thanks to the folks at TechLifeWeb</a>.</p><p>It started as a bookmarklet, but the endpoint URL for the mobile share form is easily extracted from there (<em>note: this works, ok but is not ideal… see Update #2 below</em>):</p><blockquote><p><a href="https://m.google.com/app/plus/x/?content=">https://m.google.com/app/plus/x/?content=</a><strong>CONTENT+AND+URL+GOES+HERE</strong>&amp;v=compose&amp;hideloc=1</p></blockquote><p>We’ve gone ahead and implemented Google+ now, so if you are a fan… happy linking!</p><p><strong>Update #1:</strong> In playing around with it a bit more, there’s a strange behavior whereby Google says there’s a problem with the post, but it actually does get into your Stream. I’ll dig around a bit and see if I can sort that out.</p><p><strong><strong>Update #2:</strong></strong> Google is finally supporting this properly. Here’s the URL scheme:</p><blockquote><p><a href="https://plus.google.com/share?url=">https://plus.google.com/share?url=</a><strong>URL+GOES+HERE</strong></p></blockquote>]]></content><amg:summary><![CDATA[When we launched the mobile-first version of this blog, we opted not to include Google+ as one of the sharing options because there was no way to make it work without JavaScript (a fact which undermined both our progressive enhancement…]]></amg:summary><summary type="html"><![CDATA[<p>When we launched the mobile-first version of this blog, we opted not to include Google+ as one of the sharing options because there was no way to make it work without JavaScript (a fact which undermined both our progressive enhancement…</p>]]></summary><category term="progressive enhancement" /><category term="privacy" /><category term="user experience" /></entry><entry><id>https://www.aaron-gustafson.com/notebook/what-are-they-thinking/</id><title type="html"><![CDATA[✍🏻 What are they thinking?]]></title><link href="https://www.aaron-gustafson.com/notebook/what-are-they-thinking/" rel="alternate" type="text/html" /><published>2005-11-05T21:12:02Z</published><content type="html" xml:base="https://www.aaron-gustafson.com"><![CDATA[<p>I knew things had taken a turn for the worse when <abbr title="Digital Millenium Copyright Act">DMCA</abbr> passed and media owners were discussing their desire to spy on consumers’ computers in search of illicit media, but who knew it had gotten <a href="http://www.sysinternals.com/blog/2005/10/sony-rootkits-and-digital-rights.html">this bad</a>?</p>]]></content><amg:summary><![CDATA[I knew things had taken a turn for the worse when DMCA passed and media owners were discussing their desire to spy on consumers’ computers in search of illicit media, but who knew it had gotten this bad ?]]></amg:summary><summary type="html"><![CDATA[<p>I knew things had taken a turn for the worse when DMCA passed and media owners were discussing their desire to spy on consumers’ computers in search of illicit media, but who knew it had gotten this bad ?</p>]]></summary><category term="security" /><category term="privacy" /><category term="society" /></entry></feed>