<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marina</title>
    <description>The latest articles on DEV Community by Marina (@hybridcattt).</description>
    <link>https://dev.to/hybridcattt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F460525%2F8e122273-76e8-4f24-bd5b-3242c28edc49.jpeg</url>
      <title>DEV Community: Marina</title>
      <link>https://dev.to/hybridcattt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hybridcattt"/>
    <language>en</language>
    <item>
      <title>Debugging on iOS 14 with Xcode 11</title>
      <dc:creator>Marina</dc:creator>
      <pubDate>Tue, 13 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/hybridcattt/debugging-on-ios-14-with-xcode-11-38kl</link>
      <guid>https://dev.to/hybridcattt/debugging-on-ios-14-with-xcode-11-38kl</guid>
      <description>&lt;p&gt;Every year we get a new major iOS version to test our apps on. The lucky ones can immediately upgrade to the newest Xcode 12, building against the latest iOS 14 SDK. Some other, larger projects can take a while to get upgraded. Those projects have to be built with Xcode 11 in the meantime. But even though those apps can’t be upgraded yet, they are still expected to work well on the newest iOS. And solving problems and bugs requires debugging.&lt;/p&gt;

&lt;p&gt;I recently faced a rare camera bug that only reproduced on iPhone 11 Pro with iOS 14. The app hasn’t been upgraded yet and was built with Xcode 11. The bug was mission-critical and we couldn’t afford to wait until we upgrade to Xcode 12.&lt;/p&gt;

&lt;p&gt;Out of the box, older Xcode versions can’t work with iOS 14 at all. However, with some tricks, I could not only run on iOS 14 but also debug with breakpoints and much more.&lt;/p&gt;

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

&lt;p&gt;A usual run action in Xcode consists of a few independent steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building for the device.&lt;/li&gt;
&lt;li&gt;Installing the app on the device.&lt;/li&gt;
&lt;li&gt;Launching the app.&lt;/li&gt;
&lt;li&gt;Attaching a debugger.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps rely on Xcode being able to communicate with the physical device, and the communication interface can change between iOS versions. So debugging an app built with an older version of Xcode requires a few tricks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and installing a debug build to iOS 14
&lt;/h2&gt;

&lt;p&gt;Being able to run against the newest iOS version is a problem that we have to fix every year. Thankfully, the same solution works every time. An Xcode application bundle contains support files for each iOS version it knows how to work with. Adding support for iOS 14 to Xcode 11 is a matter of copying device support files for iOS 14 into Xcode 11. Commonly, these device support files can be copied from Xcode 12 installed side by side, copied from a coworker’s machine, or downloaded from a popular shared repo.&lt;br&gt;&lt;br&gt;
It’s been already widely discussed, so here’s an article I like on the topic: &lt;a href="https://faizmokhtar.com/posts/how-to-fix-xcode-could-not-locate-device-support-files-error-without-updating-your-xcode/"&gt;How to Fix Xcode: “Could Not Locate Device Support Files” Error&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Launching the app
&lt;/h2&gt;

&lt;p&gt;With the default setup, a debug app build will automatically try to launch on the selected device after installation. Unfortunately, Xcode 11 doesn’t know how to launch apps on iOS 14, so we get this annoying error alert every time: “Failed to start remote service on device. Please check your connection to your device.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--voPRSteD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/failed_to_start_error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--voPRSteD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/failed_to_start_error.png" alt="Error: Failed to start remote service on device. Please check your connection to your device."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can still manually launch the app, though. And we can avoid the error alert popping up every time by disabling auto-launching. This behavior can be changed in scheme settings by disabling the “Debug executable” checkbox:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9t7MSln5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/scheme_settings_disable_debug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9t7MSln5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/scheme_settings_disable_debug.png" alt="Disabling debug executable in scheme settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will prevent the app from auto-launching and trying to attach a debugger.&lt;/p&gt;

&lt;p&gt;Installing the app will kill the app if it’s already running. That’s one way to know that it’s been re-installed successfully. From there, it’s just a matter of tapping that icon to launch by hand.&lt;/p&gt;
&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;After the app has been built with Xcode 11, installed and manually launched on the device like mentioned above, the app can be tested.&lt;/p&gt;

&lt;p&gt;If we also want to look at logs, we can look at them in the Console app. To have app’s logs show up there, we need to log them using the relatively new system &lt;code&gt;os&lt;/code&gt; framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
...
func logError(_ msg: StaticString, _ params: Any...) {
    os_log(msg, log: OSLog.default, type: .error, params)
}
...
logError("Value: %{public}@", property)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;%{public}@&lt;/code&gt; instead of just &lt;code&gt;%@&lt;/code&gt; allows us to see the variables even with no debugger attached. Variables are private by default to prevent leaking sensitive data via logs.&lt;a href="https://www.avanderlee.com/workflow/oslog-unified-logging/"&gt;Read more about unified logging on SwiftLee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Device logs can be examined with the possibility to filter by many parameters such as app name, log level, and many more:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bbfOz0IE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/console_filters2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bbfOz0IE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/console_filters2.png" alt="Console app with various log filters"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I logged my app’s messages with log level &lt;code&gt;.error&lt;/code&gt; because they have a distinct yellow dot next to each message, making it easier to filter out the majority of system messages.&lt;/p&gt;

&lt;p&gt;It’s worth mentioning that messages logged with &lt;code&gt;NSLog&lt;/code&gt; will also show up in Console app. I don’t recommend using &lt;code&gt;NSLog&lt;/code&gt; in Swift code, as &lt;code&gt;os_log&lt;/code&gt; is the preferred way of logging on Apple platforms these days.&lt;/p&gt;




&lt;p&gt;So far we could build, launch, and test the app on iOS 14, and examine the logs using the system Console app, all while using Xcode 11 exclusively.&lt;/p&gt;

&lt;p&gt;But sometimes just logs are not enough - debugging with breakpoints is often necessary for bug investigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Breakpoints
&lt;/h2&gt;

&lt;p&gt;Unfortunately, Xcode 11 doesn’t know how to debug apps on iOS 14. But Xcode 12 does! To get breakpoints to work, we have to use Xcode 12 for this step.&lt;/p&gt;

&lt;p&gt;There are two options to get the debugger running for an app already compiled with Xcode 11. We can either attach a debugger to an already running app, or let Xcode 12 also launch the app and attach the debugger for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Launching from Xcode 12
&lt;/h3&gt;

&lt;p&gt;After building the target with Xcode 11 (cmd+B), switch to Xcode 12, and do &lt;code&gt;Run Without Building&lt;/code&gt;by going to menu option &lt;code&gt;Product &amp;gt; Perform Action &amp;gt; Run Without Building&lt;/code&gt; or using cmd+control+R. This will install and launch the app, and attach the debugger.&lt;/p&gt;

&lt;p&gt;Using this method, we don’t need to disable auto-launching or debugging in scheme settings, because we’re using Xcode 12 for this step and it knows how to talk with iOS 14 devices. There’s a downside however, that we would then need to use Xcode 12 for running the app. &lt;em&gt;I would recommend going this way only if you always need breakpoints and the hassle of switching between Xcode versions all the time is worth it for you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Big thanks to &lt;a href="https://twitter.com/geoffhackworth"&gt;Geoff Hackworth&lt;/a&gt; for suggesting this trick!&lt;/p&gt;

&lt;h3&gt;
  
  
  Attaching debugger to a running app
&lt;/h3&gt;

&lt;p&gt;If for some reason you don’t want to use Xcode 12 for running, it’s possible to attach a debugger to an already launched app (a running process) manually.&lt;/p&gt;

&lt;p&gt;At any point of testing the app, we can open the project in Xcode 12 and attach the debugger by going to menu option &lt;code&gt;Debug &amp;gt; Attach to Process&lt;/code&gt; and picking the app’s process. The app name should appear under “Likely targets”. It might take a couple of attempts, but it works!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BgGkgQDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/debugger_likely_targets.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BgGkgQDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/debugging-ios14-xcode11/debugger_likely_targets.png" alt="App name shows under Likely Targets debug menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of debugging on Xcode 12
&lt;/h3&gt;

&lt;p&gt;With the debugger attached (either by running from Xcode 12 or manually attaching), breakpoints can be navigated as usual. We can pause anywhere, step over, step in, etc. We can also see stack traces normally. We can debug view hierarchy, explore the memory graph, and even override environment settings such as text size or dark mode, all while running a debug build created with Xcode 11 on an iOS 14 device.&lt;/p&gt;

&lt;p&gt;There is one limitation - when paused on a breakpoint, access to variables is quite limited. Most Swift variables can’t be looked into, and debugger commands such as &lt;code&gt;po&lt;/code&gt; don’t work. That’s because of this error: &lt;code&gt;Cannot load Swift type information; AST validation error in &amp;lt;...&amp;gt;: The module file format is too old to be used by this version of the debugger&lt;/code&gt;. However, &lt;code&gt;po&lt;/code&gt; seems to work while in UI debugger, and we can write any Objective-C code there. It’s not ideal, but it’s something we can work with.&lt;/p&gt;

&lt;p&gt;For cases when debugger is failing to access Swift variables, good old logging can help, as described above.&lt;/p&gt;

&lt;p&gt;Even though debugging functionality is somewhat limited, often just being able to pause, step through code paths, and see stack frames is more than enough to find the cause of a bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prevent accidental rebuilding with Xcode 12
&lt;/h3&gt;

&lt;p&gt;Since we’re building with Xcode 11, but running and debugging on Xcode 12, we might accidentally rebuild on Xcode 12 and end up testing a very different build of the app than originally intended. To avoid accidentally building with Xcode 12 while it’s used for debugging, we can add a conditional compilation error for that case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#if compiler(&amp;gt;=5.3)
#error("This project should not be built on Xcode 12")
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This piece of code can be placed anywhere in the source. The &lt;code&gt;#error&lt;/code&gt; directive is skipped when the source code is compiled with the Swift compiler of any version lower than 5.3, which corresponds to Xcode 11 or older. This way, it’s not even technically possible to accidentally build on Xcode 12.&lt;/p&gt;

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

&lt;p&gt;Even those of us who are not so lucky to be able to upgrade to Xcode 12 right away can run and debug apps on devices running iOS 14. It’s possible to fully stick to Xcode 11, occasionally resorting to Xcode 12 for breakpoints and extra things such as UI debugger. I was able to track down my critical bug and fix it, and I hope these tricks can help someone else one day too :)&lt;/p&gt;

</description>
      <category>iosdevelopment</category>
      <category>xcode</category>
      <category>ios14</category>
    </item>
    <item>
      <title> "Building a Well-Rounded Website: Essentials"</title>
      <dc:creator>Marina</dc:creator>
      <pubDate>Sun, 30 Aug 2020 15:47:19 +0000</pubDate>
      <link>https://dev.to/hybridcattt/building-a-well-rounded-website-essentials-31e</link>
      <guid>https://dev.to/hybridcattt/building-a-well-rounded-website-essentials-31e</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SreiZnrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/website-essentials/img.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SreiZnrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hybridcattt.com/assets/posts/website-essentials/img.jpg" alt="Woman coding in an IDE"&gt;&lt;/a&gt;&lt;br&gt;
&lt;i class="subtle-text"&gt;Photo by &lt;a href="https://unsplash.com/@crew?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Crew&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/website?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;I first learned the basics of web development in the 2000s, and have since contributed to web applications code in my corporate jobs and worked with HTML&amp;amp;CSS in the context of ebooks. &lt;br&gt;
But the first real website I made and published from scratch was the first version of my personal site that I built two years ago. &lt;br&gt;
It was a single &lt;code&gt;index.html&lt;/code&gt; with a static layout and very little content.&lt;br&gt;
The blog that you are reading right now is built with a static site generator. &lt;/p&gt;

&lt;p&gt;During the last couple of years, I have accumulated a large collection of bookmarks that I find helpful when making websites. &lt;br&gt;
I regularly keep referring to many of them. &lt;/p&gt;

&lt;p&gt;All of these resources are relevant to most kinds of websites, regardless of what stack or framework you use.&lt;br&gt;
If you are building a website on your own, I hope this collection will prove to be useful.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All links are from my personal collection. I have no affiliation with any of the linked resources. &lt;br&gt;
All linked resources are free to use. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Page Layout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Flexbox
&lt;/h3&gt;

&lt;p&gt;Table-based layouts are a thing of the past, with Flexbox being the most common way to layout web UIs these days. &lt;br&gt;
&lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;A Complete Guide to Flexbox&lt;/a&gt; ended up being one of the resources I get back to the most. &lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Selectors
&lt;/h3&gt;

&lt;p&gt;There's a lot of ways to apply styles to an element in CSS. &lt;br&gt;
I love the &lt;a href="https://www.w3schools.com/cssref/css_selectors.asp"&gt;CSS selector reference from w3schools&lt;/a&gt;. It provides a short overview of each selector and lets me quickly choose the best option. &lt;/p&gt;

&lt;h3&gt;
  
  
  HTML &amp;amp; Accessibility
&lt;/h3&gt;

&lt;p&gt;While browsing Twitter one day I stumbled upon an article recommendation from &lt;a href="https://twitter.com/ReyTheDev/status/1294148221992935424"&gt;@ReyTheDev&lt;/a&gt; for &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML"&gt;structuring HTML semantically&lt;/a&gt; to achieve low-effort accessibility and reader mode support. &lt;br&gt;
It's a gold-mine when it comes to the basics of modern HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Opening external links
&lt;/h3&gt;

&lt;p&gt;It was a discovery that opening links in a new tab is unfriendly to accessibility and mobile usage. &lt;br&gt;
This is usually achieved with &lt;code&gt;target="_blank"&lt;/code&gt; on links. &lt;br&gt;
I've never looked at it in such a way that the user can always opt-in to open something in a new tab, but they can never opt-out. &lt;br&gt;
&lt;a href="https://uxdesign.cc/linking-to-a-new-tab-vs-same-tab-f88b495d2187"&gt;UX Collective wrote a comprehensive article on opening links&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO
&lt;/h2&gt;

&lt;p&gt;To make a site searchable through search engines, it is essential to get several meta properties right. &lt;br&gt;
Shout out to &lt;a href="https://twitter.com/SerinnahHuber"&gt;@MarinaHuber&lt;/a&gt; for introducing me to this comprehensive &lt;a href="https://www.seobility.net/en/seocheck/"&gt;SEO check tool&lt;/a&gt;. It checks not only the technical side, but also the content itself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Canonical URLs&lt;/em&gt; help avoid duplicate content on the web. They are useful even if you post only in one place. Check out &lt;a href="https://yoast.com/rel-canonical/"&gt;rel=canonical: the ultimate guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Art
&lt;/h2&gt;

&lt;p&gt;It's hard to avoid needing graphic resources, even when building a simple website. &lt;br&gt;
There are many copyright-free collections out there, for example &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;, &lt;a href="https://undraw.co/illustrations"&gt;Undraw&lt;/a&gt; and &lt;a href="https://www.blackillustrations.com"&gt;Black Illustrations&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Favicons
&lt;/h3&gt;

&lt;p&gt;The quickest way to get favicons is to use an online generator. &lt;br&gt;
&lt;a href="https://favicon.io"&gt;favicon.io&lt;/a&gt; lets you make favicons from text, emojis, and images. For example, I made my favicon from the text "MG" - creative, right! &lt;br&gt;
Files will be ready-to-go along with a code snippet. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://realfavicongenerator.net/favicon_checker"&gt;Favicon checker&lt;/a&gt; is super handy for verifying that all favicons and manifests are in place. &lt;/p&gt;

&lt;h3&gt;
  
  
  Icons
&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://simpleicons.org"&gt;Simple Icons&lt;/a&gt; has a comprehensive catalog of free SVG icons for popular brands. &lt;/p&gt;

&lt;h3&gt;
  
  
  Colors
&lt;/h3&gt;

&lt;p&gt;I've tried several color scheme generators before ending up with &lt;a href="https://coolors.co"&gt;Coolors&lt;/a&gt;. &lt;br&gt;
You can pick a palette from a large catalog, modify or create your own, even based on an uploaded image. &lt;/p&gt;

&lt;h2&gt;
  
  
  Social Sharing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Buttons
&lt;/h3&gt;

&lt;p&gt;Twitter's sharing widget and similar components from other social networks have tracking embedded, so I opted out of using them. &lt;br&gt;
I am also avoiding using unnecessary cookies or any kind of local storage, so I wouldn't have to show the infamous cookie banner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ahmad-osman.com/en/blogs/social-media-share-without-javascript/"&gt;Social Media share buttons without javascript&lt;/a&gt; documents sharing links for many popular social apps and networks. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://sharingbuttons.io"&gt;Sharing button generator&lt;/a&gt; is a great starting point for rolling your own buttons. &lt;br&gt;
I had to tweak the CSS, but it was still easier than starting from scratch. &lt;/p&gt;

&lt;h3&gt;
  
  
  Preview cards
&lt;/h3&gt;

&lt;p&gt;Most social networks use Open Graph metadata to generate preview cards. &lt;br&gt;
&lt;a href="https://ogp.me"&gt;Open Graph Protocol homepage&lt;/a&gt; gives a nice overview of the generic webpage properties and directions to dig deeper. &lt;/p&gt;

&lt;p&gt;Twitter has its own meta properties inspired by Open Graph. The main addition is that you can specify the style you want your cards to follow.&lt;br&gt;
Here's the link to &lt;a href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started"&gt;Twitter's Documentation on Cards&lt;/a&gt;. Other social networks such as &lt;a href="https://www.linkedin.com/help/linkedin/answer/46687/making-your-website-shareable-on-linkedin?lang=en"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://developers.facebook.com/docs/sharing/webmasters/"&gt;Facebook&lt;/a&gt; rely on Open Graph protocol and don't have their own properties sans rare exceptions. &lt;/p&gt;

&lt;h4&gt;
  
  
  Testing preview cards
&lt;/h4&gt;

&lt;p&gt;Testing preview cards is something that I did a lot of, making sure I like how my site looks when shared. &lt;br&gt;
I certainly did send messages to myself, nothing wrong with that :)&lt;/p&gt;

&lt;p&gt;Each of the major social networks has its own &lt;em&gt;card validator&lt;/em&gt;, which lets you easily preview the cards. I only have &lt;a href="https://cards-dev.twitter.com/validator"&gt;Twitter Card Validator&lt;/a&gt; bookmarked, but here's also &lt;a href="https://www.linkedin.com/post-inspector/"&gt;LinkedIn Post Inspector&lt;/a&gt; and &lt;a href="https://developers.facebook.com/tools/debug/"&gt;Facebook Sharing Debugger&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Card metadata is heavily cached on the social network's side, so they don't have to hit your webpage every time someone is looking at a post with your link. &lt;br&gt;
Card validators have a hidden perk: they also invalidate the respective caches.&lt;br&gt;
This article goes in-depth on cards and caching: &lt;a href="https://www.socialmediaexaminer.com/how-to-clear-facebook-cache-twitter-cache-linkedin-cache/"&gt;How to Clear Facebook Cache, Twitter Cache, and LinkedIn Cache so Your Content Looks Right&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking
&lt;/h2&gt;

&lt;p&gt;I care about privacy, so having Google Analytics on the previous version of my site was bugging me. &lt;br&gt;
Once I randomly stumbled upon &lt;a href="https://www.goatcounter.com"&gt;GoatCounter&lt;/a&gt; - privacy-friendly open source web analytics.&lt;br&gt;
&lt;a href="https://github.com/zgoat/goatcounter/blob/master/docs/rationale.markdown"&gt;GoatCounter's rationale&lt;/a&gt; resonated with me, and now it is my go-to analytics service. By the way, it is completely free for non-commercial use. &lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://humans.fyi"&gt;humans.fyi&lt;/a&gt; is a huge catalog of personal websites, where you can browse by platform, dominant color, and even occupation. Exploring other people's sites gives endless inspiration for layouts, color schemes, and fonts. And a little bit of FOMO.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;A Complete Guide to Flexbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3schools.com/cssref/css_selectors.asp"&gt;CSS selector reference from w3schools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML"&gt;Structuring HTML semantically&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://uxdesign.cc/linking-to-a-new-tab-vs-same-tab-f88b495d2187"&gt;Linking to a new tab vs. same tab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.seobility.net/en/seocheck/"&gt;SEO check tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yoast.com/rel-canonical/"&gt;rel=canonical: the ultimate guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://unsplash.com"&gt;Unsplash - freely usable images&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://undraw.co/illustrations"&gt;Undraw - open-source illustrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.blackillustrations.com"&gt;Black Illustrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://favicon.io"&gt;favicon.io - favicon generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://realfavicongenerator.net/favicon_checker"&gt;Favicon checker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpleicons.org"&gt;Simple Icons - svgs for popular brands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://coolors.co"&gt;Coolors - color scheme generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sharingbuttons.io"&gt;Sharing button generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ahmad-osman.com/en/blogs/social-media-share-without-javascript/"&gt;Social Media share buttons without javascript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ogp.me"&gt;Open Graph Protocol homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started"&gt;Twitter's Documentation on Cards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cards-dev.twitter.com/validator"&gt;Twitter Card Validator&lt;/a&gt;, &lt;a href="https://www.linkedin.com/post-inspector/"&gt;LinkedIn Post Inspector&lt;/a&gt;, &lt;a href="https://developers.facebook.com/tools/debug/"&gt;Facebook Sharing Debugger&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.socialmediaexaminer.com/how-to-clear-facebook-cache-twitter-cache-linkedin-cache/"&gt;How to Clear Facebook Cache, Twitter Cache, and LinkedIn Cache so Your Content Looks Right&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goatcounter.com"&gt;GoatCounter web analytics&lt;/a&gt;, &lt;a href="https://github.com/zgoat/goatcounter/blob/master/docs/rationale.markdown"&gt;GoatCounter's rationale&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://humans.fyi"&gt;humans.fyi - catalog of personal websites&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading 🙌 &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
