<?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: Navillus</title>
    <description>The latest articles on DEV Community by Navillus (@navillus_dev).</description>
    <link>https://dev.to/navillus_dev</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%2F669404%2Fa4c73c9a-7a57-4cea-bf4d-b73c214d6dba.png</url>
      <title>DEV Community: Navillus</title>
      <link>https://dev.to/navillus_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/navillus_dev"/>
    <language>en</language>
    <item>
      <title>The power of simplicity</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Sat, 07 Aug 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/the-power-of-simplicity-4i5i</link>
      <guid>https://dev.to/navillusbv/the-power-of-simplicity-4i5i</guid>
      <description>&lt;p&gt;Our last post highlighted the benefits of using &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt; and &lt;a href="https://forestry.io"&gt;Forestry CMS&lt;/a&gt; (catch up &lt;a href="https://dev.to/blog/astro-plus-forestry-revisited"&gt;here&lt;/a&gt; in case you missed it). Forestry actually shared our post with a really important point that I thought was worth digging into with a proper blog post.&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;The beauty of embracing open formats like Markdown and JSON on top of Git is that a CMS you designed more than 5 years ago, works perfectly with the latest modern web tooling.&lt;br&gt;&lt;br&gt;Now go check &lt;a href="https://twitter.com/astrodotbuild?ref_src=twsrc%5Etfw"&gt;@astrodotbuild&lt;/a&gt; to ship faster websites 🚀 &lt;a href="https://t.co/0o6OgCfkgx"&gt;&lt;/a&gt;&lt;a href="https://t.co/0o6OgCfkgx"&gt;https://t.co/0o6OgCfkgx&lt;/a&gt;&lt;/p&gt;— Forestry CMS (&lt;a class="mentioned-user" href="https://dev.to/forestryio"&gt;@forestryio&lt;/a&gt;
) &lt;a href="https://twitter.com/forestryio/status/1423251438063521792?ref_src=twsrc%5Etfw"&gt;August 5, 2021&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;Follow &lt;a href="https://twitter.com/forestryio"&gt;@forestryio&lt;/a&gt; on Twitter if you don't already! Their git-based CMS tools are a huge win for any Jamstack project and should you have any questions or issues, their team is extremely responsive and helpful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; The web development landscape is constantly in flux. It's easy to see the latest tools, languages, and frameworks get hyped on your favorite blogs and podcasts, but at the end of the day you probably need to build quality sites that can be easily maintained for years. More often than not, the boring tools make your life easier in the long run!&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of history
&lt;/h2&gt;

&lt;p&gt;It wasn't so long ago that sites were edited by handed - good old fashioned HTML,CSS, and (maybe) JS. A site contained more &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;s than pages, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee"&gt;RIP marquee!&lt;/a&gt;, and the &lt;code&gt;querySelector&lt;/code&gt; wasn't a thing.&lt;/p&gt;

&lt;p&gt;Browsers are still fundamentally the same today. Yes they are much bigger and feature packed, but at the end of the day a browser takes markup in the form of HTML, styles in the form of CSS, and JavaScript for interactivity. We have more APIs to play with, and more caching problems to manage, but at the end of the day it's the development workflow that has really complicated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern web development
&lt;/h3&gt;

&lt;p&gt;Jumping ahead a few years (or 20) and now its not uncommon to start a project by deciding on the 5+ tools and platforms you will be bolting together. Do you want static site generation (SSG), server-side rendering (SSR), or client-side rendering(CSR)? Are you standing up and maintaining a backend server, or jumping on the serverless backend? Will plain CSS do the trick, or is SASS/SCSS/LESS your preferred approach? What about this CSS in JS and styled components madness?!?&lt;/p&gt;

&lt;p&gt;Oh wait, you also need to host it somewhere. Does the project need continuous integration (CI) with automated testing, automated deployments, staging environments, etc? Better schedule another few days to decide on those tools, setup accounts, and bootstrap the basics together before you start coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it have to be so complicated?
&lt;/h2&gt;

&lt;p&gt;No! Sure, all these tools and processes were created for good reason. CI can save a huge amount of time. Transpilers, build tools, and linters can help catch common mistakes and enforce team coding standards. Frontend frameworks can really be a lifesaver, especially if you need to build interfaces with complex interactivity and state management.&lt;/p&gt;

&lt;p&gt;But all these advancements can turn into a real nightmare, too. Ever tried onboarding a new hire that is an experienced web developer but not familiar with the latest React trends like styled components or hooks? Is the time spent ramping up on these abstractions really worth the effort for a simple marketing site or statically hosted e-commerce store?&lt;/p&gt;

&lt;h3&gt;
  
  
  Long-term value
&lt;/h3&gt;

&lt;p&gt;The tech stack you chose can be a much larger commitment than it first seems to be. I would be very surprised if react was no longer used a couple years down the road, but what about the 15 libraries you pull in to that react app?&lt;/p&gt;

&lt;p&gt;If a project is built on an &lt;em&gt;everything but the kitchen sink™&lt;/em&gt; app platform like &lt;a href="https://nuxtjs.org/"&gt;Nuxt.js&lt;/a&gt;, &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, or &lt;a href="https://kit.svelte.dev"&gt;SvelteKit&lt;/a&gt; can really let you hit the ground running, but will they still be supported a few years down the road? Will developers still be excited to work with them, or will it be the butt of too many developers' jokes like WordPress?&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it simple, stupid
&lt;/h2&gt;

&lt;p&gt;Back to the entire reason we're here, sticking with "boring" tech may not be such a bad idea. The &lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt; spec is around 20 years old, &lt;a href="https://en.wikipedia.org/wiki/Git"&gt;git&lt;/a&gt; is 16 years old, and &lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;markdown&lt;/a&gt; is 17 years old. Though the HTML/CSS/JS standards are a constant work in progress they go back as far as &lt;a href="https://en.wikipedia.org/wiki/HTML"&gt;1993&lt;/a&gt; (ok, 1995 for &lt;a href="https://en.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Even better, these are all open standards that may evolve but will never go away.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about this site though?
&lt;/h3&gt;

&lt;p&gt;This very site is built with a shiny new SSG, &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;, isn't that a bit hypocritical? I don't think so, but hear me out!&lt;/p&gt;

&lt;p&gt;Take a look at the &lt;a href="https://github.com/navillus-bv/navillus-dev"&gt;sourcecode&lt;/a&gt; for this project and you'll see that the real meat of the site is based entirely on the standards above. SCSS is used for styling, but honestly the only reason we even used that was because CSS nesting isn't supported yet.&lt;/p&gt;

&lt;p&gt;Most of the site's pages are authored in markdown and various bits of data are stored in JSON.&lt;/p&gt;

&lt;p&gt;Page templates and components are in &lt;code&gt;.astro&lt;/code&gt; files that really just consists of JavaScript in the form of &lt;a href="https://docs.astro.build/core-concepts/astro-components#frontmatter-script"&gt;frontmatter&lt;/a&gt;, styles are either in global &lt;code&gt;.css&lt;/code&gt; files or component-level &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks, and markup is mostly HTML with a bit of JSX-like javascript inline for conditionals and for-each loops.&lt;/p&gt;

&lt;p&gt;It would take a matter of a few hours to migrate the entire site to a different SSG, or even an entirely different platform like &lt;a href="https://nextjs.org"&gt;Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So yes, we're technically using a few new build tools to wire templates together, but at the end of the day it's easy to use Astro and forget that you aren't using the basic, web-native HTML/CSS/JS stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Innovation is amazing, just don't get stuck
&lt;/h2&gt;

&lt;p&gt;It's a very common mistake, especially for developers newer to web development, to get stuck on the hamster wheel chasing the latest toy. Don't avoid innovation, just be strategic and selective in where you invest your time and efforts!&lt;/p&gt;

&lt;p&gt;There's always a new tool to try or pattern to learn, but at the end of the day it's much more satisfying to ship a product rather than trying out a new tool with sample projects that never sees the light of day!&lt;/p&gt;

&lt;p&gt;I feel like I bashed on react here - don't get me wrong, react is an excellent and extremely powerful tool. React still has a huge lead in usage compared to the other frontend frameworks and, especially if you're in the job market, it's much easier to find job listing that require react experience than listings that don't mention react at all. But if you find yourself running in circles, getting frustrated with a build pipeline that's fighting you or magic hooks that you don't understand, take a step back and learn the basic (boring) 20 year old tools. Ship fast, don't break the build!&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>instro</category>
    </item>
    <item>
      <title>Astro + Foresty CMS Revisited</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Tue, 03 Aug 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillus_dev/astro-foresty-cms-revisited-ji2</link>
      <guid>https://dev.to/navillus_dev/astro-foresty-cms-revisited-ji2</guid>
      <description>&lt;p&gt;It's been just over a month since the original &lt;a href="https://dev.to/blog/astro-plus-forestry"&gt;Astro + Forestry CMS&lt;/a&gt; demo, but things have been &lt;a href="https://github.com/snowpackjs/astro/blob/main/packages/astro/CHANGELOG.md"&gt;moving quickly&lt;/a&gt; in Astro land! We'll build upon the original demo, go ahead and check out the first post if you haven't done so already!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; We received some great feedback from &lt;a href="https://twitter.com/forestryio"&gt;Forestry&lt;/a&gt; after the original demo was released. I had been holding off on revisiting that post until a few Astro features were released. Check out the &lt;a href="https://demo-astro-forestry.netlify.app"&gt;live demo&lt;/a&gt;, or dive into the main &lt;a href="https://github.com/Navillus-BV/demo-astro-forestry/commit/8660fb54988390b3a27d65a3abfe784725d789df"&gt;diff&lt;/a&gt; that includes most of the updates listed below.&lt;/p&gt;

&lt;p&gt;Specifically, Astro's &lt;a href="https://docs.astro.build/core-concepts/collections"&gt;Collections API&lt;/a&gt; was updated to handle even more use cases. Oh yeah, and their &lt;a href="https://docs.astro.build/"&gt;docs site&lt;/a&gt; was launched! It's hard to believe this project was only announced a few months ago, the community has really grown quickly and countless hours were put in to build a great looking (and localized!) documentation site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback straight from the source
&lt;/h2&gt;


&lt;blockquote&gt;
&lt;p&gt;Awesome post 👏&lt;br&gt;We should give a try to &lt;a href="https://twitter.com/astrodotbuild?ref_src=twsrc%5Etfw"&gt;@astrodotbuild&lt;/a&gt; 🚀&lt;br&gt;&lt;br&gt;Minor feedback:&lt;br&gt;1. You could set /images as your default media folder instead of the default /uploads to further reduce the diff. &lt;br&gt;2. Authors could be stored as JSON file(s) instead of Markdown if you don't need a body.&lt;/p&gt;— Forestry CMS (&lt;a class="mentioned-user" href="https://dev.to/forestryio"&gt;@forestryio&lt;/a&gt;
) &lt;a href="https://twitter.com/forestryio/status/1409905329845030916?ref_src=twsrc%5Etfw"&gt;June 29, 2021&lt;/a&gt;
&lt;/blockquote&gt; 
&lt;h3&gt;
  
  
  Default media folders
&lt;/h3&gt;

&lt;p&gt;Forestry's CMS is &lt;a href="https://forestry.io/docs/quickstart/configure-cms/"&gt;extremely flexible&lt;/a&gt;, and honestly its crazy the feature set they're able to offer while storing all your data in your own git repo. &lt;em&gt;No, this isn't a paid post. I'm a user of Forestry and big fan of the git-based CMS approach!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of many options when configuring Forestry is the default folder for &lt;a href="https://forestry.io/docs/quickstart/configure-cms/#media-settings-examples"&gt;media uploads&lt;/a&gt;. I definitely had an eye towards minimizing the diff in the original demo, I'm just in the habit of using a &lt;code&gt;/uploads&lt;/code&gt; directory for user uploaded content. Old dogs, new tricks, and all that.&lt;/p&gt;
&lt;h3&gt;
  
  
  Authors stored in JSON
&lt;/h3&gt;

&lt;p&gt;This was excellent feedback, and worth digging into a little further.&lt;/p&gt;

&lt;p&gt;I originally had author information stored in separate markdown files, &lt;code&gt;/data/authors/don.md&lt;/code&gt; and &lt;code&gt;/data/authors/sancho.md&lt;/code&gt;.This honestly didn't make that much sense, markdown is a great way to combine properties and content (usually built to HTML). The blog demo doesn't have any author-specific content, just a few properties like &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given that the site doesn't need to pull any HTML content for the author, it makes much more sense to store that data in a simple JSON file. Let's get rid of the author markdown files entirely, replacing it with &lt;code&gt;src/data/authors.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "don": {
    "name": "Don Quixote",
    "image": "/uploads/don.jpg"
  },
  "sancho": {
    "name": "Sancho Panza",
    "image": "/uploads/sancho.jpg"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forestry supports this out of the box, once you &lt;a href="https://forestry.io/docs/quickstart/configure-cms/#setting-up-sidebar-content-sections"&gt;setup the sidebar&lt;/a&gt; to include the new JSON file it recognizes that the file is a map and it &lt;strong&gt;just works&lt;/strong&gt;. I honestly expected this to fight me a little bit, and was to when I had no issue removing references to the old markdown files. I was even able to reuse the same &lt;a href="https://forestry.io/docs/quickstart/configure-cms/#content-modeling"&gt;content model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did need to update the content model for posts to reference the new JSON file instead of a markdown file, but a few clicks in the settings menu and it was all hooked back up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus points: Instant Previews
&lt;/h3&gt;

&lt;p&gt;Forestry's &lt;a href="https://forestry.io/docs/previews/instant-previews/"&gt;instant previews&lt;/a&gt; run your development server in a docker container and allow you to preview CMS updates in realtime. That's one of those features that can push plenty of projects to use a hosted CMS platform, very cool to see live previews working so seamlessly in a git-based CMS.&lt;/p&gt;

&lt;p&gt;One issue I ran into when deploying the first demo was that Astro only supported node 14+. Instant Previews allow you to customize which docker image is used for your development server, but I couldn't quite get it to work with an early version of Astro and ran out of time. As of a couple weeks though, Astro now supports node 12 out of the box!&lt;/p&gt;

&lt;p&gt;After updating the demo project, setting up instant previews was as simple as going back to Forestry's default preview settings. I had tried a custom docker container with no luck, but the included node 12 + yarn image worked like a charm with the latest version of Astro.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Collections API
&lt;/h2&gt;

&lt;p&gt;The original collections API in Astro was designed before the beta was publicly released, and it turns out there were a few use cases that were more common than expected.&lt;/p&gt;

&lt;p&gt;There aren't any monumental changes here, you can dig through the &lt;a href="https://github.com/snowpackjs/astro/pull/703"&gt;merged RFC&lt;/a&gt; if you're curious. A few of the API names were updated to be more clear, and the API was updated to work with the newer &lt;code&gt;Astro.props&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;You can check out the &lt;a href="https://github.com/Navillus-BV/demo-astro-forestry/commit/8660fb54988390b3a27d65a3abfe784725d789df#diff-a12b9a8302a65aacc7f592f6058bbc7b2eebcc2509a70ec64f182a67c9d54e45L3"&gt;diff here&lt;/a&gt; to see exactly what I had to do to update the &lt;code&gt;$posts.astro&lt;/code&gt; route for the new API. Personally, I'm a fan of the newer design and think the code is a bit cleaner and easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Astro has been moving quickly since it's public beta launched! I was glad to see how easy it was to clean up the demo a bit and take even better advantage of Forestry. If you haven't worked with a git-based CMS before I highly recommend you take an afternoon and give it a try. It may not be right for every project, but the developer experience literally having all your CMS data on your local machine just can't be beat!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>cms</category>
    </item>
    <item>
      <title>Animated page transitions in Astro with Swup</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Sat, 24 Jul 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/animated-page-transitions-in-astro-with-swup-25kc</link>
      <guid>https://dev.to/navillusbv/animated-page-transitions-in-astro-with-swup-25kc</guid>
      <description>&lt;p&gt;I can't be the only developer that has come across a beautifully designed website with top notch animations only to burn an afternoon trying to figure out how exactly they did it. Usually the answer is a combination of 5 different libraries and a pile of JavaScript and CSS being sent over the wire.&lt;/p&gt;

&lt;p&gt;Animations don't have to be all or nothing though, and it really can be best to start small and slowly grow and polish the user experience. &lt;a href="https://swup.js.org"&gt;Swup&lt;/a&gt; is an excellent starting point, offering all the basics you need to hook up client-side routing with page transition animations. Even better, Swup's API supports &lt;a href="https://swup.js.org/plugins"&gt;plugins&lt;/a&gt; with over a dozen pre-made plugins for the most common use cases and a simple API to roll your own custom plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Adding page transition animations to &lt;a href="https://astro.build"&gt;Astro's&lt;/a&gt; &lt;a href="https://github.com/snowpackjs/astro/tree/main/examples/blog-multiple-authors"&gt;blog-multiple-authors&lt;/a&gt; example was surprisingly simple. Check out the &lt;a href="https://github.com/Navillus-BV/demo-astro-swup"&gt;source code here&lt;/a&gt; or jump right into the &lt;a href="https://demo-astro-swup.netlify.app/"&gt;live demo&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;There's very little setup here since we're starting with an existing Astro starter. First, let's use &lt;a href="https://github.com/Rich-Harris/degit"&gt;degit&lt;/a&gt; to create a local copy of the blog example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx degit snowpackjs/astro/examples/blog-multiple-authors demo-astro-swup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prepping for swup.js
&lt;/h3&gt;

&lt;p&gt;There are great docs on &lt;a href="https://swup.js.org/getting-started"&gt;swup's site&lt;/a&gt; so I won't get too in the weeds, but it's worth having a basic idea of how the routing will work.&lt;/p&gt;

&lt;p&gt;When a visitor clicks a link on your site, swup will hijack that request and attempt to load the next page in the background. By default swup looks for an element with the ID &lt;code&gt;#swup&lt;/code&gt; on every page (this is configurable). When navigating to a new page, swup loads the new content in the background and swaps out all the old content inside the &lt;code&gt;#swup&lt;/code&gt; element for the content in the new page.&lt;/p&gt;

&lt;p&gt;During the navigation, CSS classes like &lt;code&gt;is-animating&lt;/code&gt; and &lt;code&gt;is-leaving&lt;/code&gt; are added giving you hooks to trigger the actual transition animations.&lt;/p&gt;

&lt;p&gt;With that said, it'll be a little easier to add swup to every page on your site if they are all using a shared layout component in the Astro project. See &lt;a href="https://github.com/Navillus-BV/demo-astro-swup/commit/792e8f996870166684f2299ac315ca7f82d72b39"&gt;this commit&lt;/a&gt; if you're curious exactly what I moved around to create the common layout component in this project.&lt;/p&gt;

&lt;p&gt;Most importantly, in &lt;code&gt;src/layouts/Main.astro&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"swup"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the &lt;code&gt;#swup&lt;/code&gt; element is on every page and wraps all page-specific content that should be replaced during a page navigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding swup.js
&lt;/h2&gt;

&lt;p&gt;Swup is extremely configurable, but one NPM dependency and a couple lines of code is really all it takes to get started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; swup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize swup in &lt;code&gt;/public/app.js&lt;/code&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, import &lt;code&gt;app.js&lt;/code&gt; in the shared layout...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    ...
    &amp;lt;script type="module" src="/app.js"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding transition animations
&lt;/h3&gt;

&lt;p&gt;Ok, so that won't actually animate anything yet. Check out swup's &lt;a href="https://swup.js.org/themes/create-theme"&gt;theme docs&lt;/a&gt; if you want to write your own CSS animations, but they do include a few basic themes on NPM. We'll stick with the basic slide theme for now, which you can see in action on the &lt;a href="https://demo-astro-swup.netlify.app/"&gt;live demo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @swup/slide-theme
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/slide-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Supported scoped styles
&lt;/h3&gt;

&lt;p&gt;Astro supports &lt;a href="https://docs.astro.build/guides/styling#scoped-styles"&gt;scoped styles&lt;/a&gt; out of the box. This can be a huge win for performance and avoiding unnecessary CSS in the browser, but we'll need swup to update any linked stylesheets when navigating to a new page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is actually a problem I ran into with &lt;a href="https://barba.js.org/"&gt;barba.js&lt;/a&gt;. I couldn't for the life of me figure out how to update the page's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; when navigating. It seems like this may have been possible in &lt;code&gt;v1&lt;/code&gt; but is no longer supported in &lt;code&gt;v2&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thankfully, swup has this covered with their &lt;a href="https://swup.js.org/plugins/head-plugin"&gt;head plugin&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @swup/head-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupHeadPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/head-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/slide-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupHeadPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plugins to the rescue again! This plugin will replace the old &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; with the version in the new page when navigating - it even has &lt;a href="https://swup.js.org/plugins/head-plugin#options"&gt;config options&lt;/a&gt; should you need to persist some styles or meta tags on every page!&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility support
&lt;/h3&gt;

&lt;p&gt;Page transitions can really ruin the experience for anyone visiting your site with an accessibility tool like a screen reader. Many accessibility tools depend on browser events to know when the page content has changed, with swup manually changing out the DOM content a reader may never realize anything changed.&lt;/p&gt;

&lt;p&gt;Again, there's a handy plugin to take care of this accessibility problem. It isn't a magic bullet and I strongly recommend you manually test all of your sites with a screen reader - if nothing else it's enlightening to see how different using the web can be if you are visually impaired!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @swup/a11y-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupA11yPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/a11y-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupHeadPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/head-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@swup/slide-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Swup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupA11yPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupHeadPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SwupSlideTheme&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Progressively enhanced, for free!
&lt;/h2&gt;

&lt;p&gt;Take a look at our &lt;a href="https://navillus.dev/blog/progressive-enhancement"&gt;Progressively enhancing Svelte with JavaScript&lt;/a&gt; post for a more detailed explanation of why progressive enhancement is so important, but suffice it to say a site should never require JavaScript to be usable.&lt;/p&gt;

&lt;p&gt;In the case of swup transitions, if JavaScript fails or is disabled for any reason we have a perfectly functional static site that can navigate to new pages like normal. Swup doesn't change your links or &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags at build time, so if &lt;code&gt;app.js&lt;/code&gt; never loads or hits an exception you're left with exactly the same static site that Astro built in the first place!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and next steps
&lt;/h2&gt;

&lt;p&gt;There's plenty more you could do here to really get crazy with animations. Pull in &lt;a href="https://greensock.com/gsap/"&gt;gsap&lt;/a&gt; to create intricate animation timelines or add transition animations to SVGs. Though I haven't tried it yet, I see no reason why you couldn't have multiple instances of swup targeting different portions of your page.&lt;/p&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/navillus_dev"&gt;Twitter&lt;/a&gt; if that's your thing, and reach out to share the animations you come up with for your own site!&lt;/p&gt;

</description>
      <category>astro</category>
    </item>
    <item>
      <title>Converting our site to Astro</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Tue, 20 Jul 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/converting-our-site-to-astro-3j0m</link>
      <guid>https://dev.to/navillusbv/converting-our-site-to-astro-3j0m</guid>
      <description>&lt;p&gt;Not too long ago, the Navillus site was rewritten from the ground up in &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;. Since then we've had quite a bit of interest in why we jumped to Astro, how it went, and what we learned. I'll do my best to answer the common questions here, but feel free to reach out on &lt;a href="https://twitter.com/navillus_dev"&gt;Twitter&lt;/a&gt; if we missed anything!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Our homepage makes fewer requests (10 vs. 21), is smaller (77kB vs. 123kB), and our JavaScript footprint went down from 31kB to a whopping 2kB!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we moved to Astro
&lt;/h2&gt;

&lt;p&gt;A huge majority of the web is static content, and our site is no different. There are exactly two components with any type of client-side interactivity, the mobile menu and our theme toggle. As you might expect, those components aren't exactly complicated and can be built with only a few lines of JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating from SvelteKit
&lt;/h2&gt;

&lt;p&gt;Our site was previously written in &lt;a href="https://kit.svelte.dev"&gt;SvelteKit&lt;/a&gt; and, especially for being it considered beta, we were very happy with SvelteKit. The fact is, though, that webapp frameworks like SvelteKit, &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, and &lt;a href="https://nuxtjs.org/"&gt;Nuxt.js&lt;/a&gt; are focused on building highly interactive frontends rather than mostly static sites.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, SvelteKit earned its place at the top of my list when building large, dynamic webapps. But for most projects, I will happily build with Astro and sprinkle in &lt;a href="https://svelte.dev"&gt;Svelte&lt;/a&gt; components when needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What did it take to change?
&lt;/h3&gt;

&lt;p&gt;Surprisingly, not that much! Most of the Svelte components we had were only used for layout purposes, and because Svelte sticks so closely to plain old HTML/CSS/JS it was trivial to convert most of the Svelte components to Astro components.&lt;/p&gt;

&lt;p&gt;The main change will be related to conditional rendering syntax, like hiding/showing an element or looping over an array of data. For example,&lt;/p&gt;

&lt;p&gt;In Svelte...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{#if open}
   ...
{/if}

&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  {#each items as item}
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{item}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  {/each}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{open &amp;amp;&amp;amp; (
  ...
)}

{items.map((item) =&amp;gt; (
  &amp;lt;li&amp;gt;{item}&amp;lt;/li&amp;gt;
))}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coming from Svelte this feels a little too React-y for my taste, but it is really handy to be able to use JavaScript right in the template. I haven't actually tried this yet, but I assume you could &lt;code&gt;sort()&lt;/code&gt; or &lt;code&gt;filter()&lt;/code&gt; the array right in your template!&lt;/p&gt;

&lt;h3&gt;
  
  
  What didn't work?
&lt;/h3&gt;

&lt;p&gt;Nothing! Our site is pretty straight forward with little more than static homepage content and a blog, those really are table stakes for any static site generator.&lt;/p&gt;

&lt;p&gt;When it came time to port over the mobile menu and theme toggle, we decided to use the Svelte components as-is initially. That's the real benefit of Astro, static site generation without having to give up the component framework when needed. Note that we did recently move those components to Astro as well, helping to bring our JS size below 2kB.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about server routes?
&lt;/h3&gt;

&lt;p&gt;This site wasn't using any of SvelteKit's &lt;a href="https://kit.svelte.dev/docs#routing-endpoints"&gt;server endpoints&lt;/a&gt;, but it is important to remember that Astro is currently focused on static site generation.&lt;/p&gt;

&lt;p&gt;Astro doesn't build any kind of server but if you happen to deploy to &lt;a href="https://netlify.com"&gt;Netlify&lt;/a&gt;, their &lt;a href="https://www.netlify.com/products/functions/"&gt;Functions&lt;/a&gt; product is a great fit for Astro. Subscribe to our &lt;a href="https://navillus.dev/feed/blog.xml"&gt;RSS feed&lt;/a&gt; so you don't miss an upcoming post adding Netlify Functions to populate our site's &lt;a href="https://webmention.io"&gt;web mentions&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  What did we learn?
&lt;/h2&gt;

&lt;p&gt;Working with Astro was refreshingly simple. React, Svelte, Vue, etc. have their place in modern web development but it's important to remember that they aren't always necessary. Our theme toggle does nothing more than add/remove a class from the document element, is that really enough to warrant another dependency and ~9kB in extra JS?&lt;/p&gt;

&lt;p&gt;Astro is still in beta! The framework has been moving extremely fast, a quick glance at the &lt;a href="https://github.com/snowpackjs/astro/blob/main/packages/astro/CHANGELOG.md"&gt;changelog&lt;/a&gt; shows 13 releases so far in the month of July. A few APIs have had small changes and the Collections API is in the middle of a &lt;a href="https://github.com/snowpackjs/astro/pull/703"&gt;refactor&lt;/a&gt;, don't be surprised if you occasionally have to make a few updates to keep up with the latest Astro release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's easy to skip right past the simple solution in software projects. Full-featured frameworks can be a huge time saver when starting a dynamic web application, but if all you need is a marketing site you may just be better off with a simple statically built site powered by Astro!&lt;/p&gt;

&lt;p&gt;Have more questions that we missed here? Reach out on &lt;a href="https://twitter.com/navillus_dev"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>astro</category>
    </item>
    <item>
      <title>Astro + Snipcart</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Sat, 03 Jul 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/astro-snipcart-10om</link>
      <guid>https://dev.to/navillusbv/astro-snipcart-10om</guid>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check out the live demo &lt;a href="https://demo-astro-snipcart.netlify.app/"&gt;here&lt;/a&gt;, or if you're feeling brave dive right into the &lt;a href="https://github.com/Navillus-BV/demo-astro-snipcart"&gt;GitHub Repo&lt;/a&gt; begging for a refactor!&lt;/p&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;When designing the tech stack for an e-commerce project, the web of competing priorities can start to feel like an &lt;a href="https://mcescher.com/"&gt;M. C. Escher&lt;/a&gt; painting.&lt;/p&gt;

&lt;p&gt;Online stores directly drive revenue through their site, making conversion rates a top priority. That often leads to the need for realtime tracking and user-specific product recommendations. Cool! We know we'll need a backend feeding the frontend realtime data.&lt;/p&gt;

&lt;p&gt;What happens, though, when your marketing campaign goes viral and site traffic takes off like a rocket? Wait a second, so is this when we're supposed to reach for cloud-based autoscaling? Or a serverless solution?&lt;/p&gt;

&lt;h2&gt;
  
  
  The benefits of Jamstack
&lt;/h2&gt;

&lt;p&gt;Static sites aren't a one size fits all solution, but they have some very real benefits especially at smaller scales. It seems crazy to use a static site for an online store though...right?&lt;/p&gt;

&lt;p&gt;I'd argue that a vast majority of online shops could see huge benefits if they switched to a static setup. Their store is always available, fast, and easily indexed by Google. Most importantly, if their next Instagram post goes viral the rush of shoppers won't take down their site and throw away all those potential sales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter Snipcart
&lt;/h3&gt;

&lt;p&gt;We have used &lt;a href="https://snipcart.com"&gt;Snipcart&lt;/a&gt; on quite a few projects over the years, including &lt;a href="https://kamfly.io"&gt;Kamfly&lt;/a&gt; and been extremely happy with the results.&lt;/p&gt;

&lt;p&gt;Snipcart is a drop-in shopping cart solution complete with support for subscriptions and digital goods. Their admin dashboard gives you easy access to customer and product analytics, inventory management, abandoned cart re-engagement campaigns, and even supports multiple currencies and tax solutions.&lt;/p&gt;

&lt;p&gt;Even better, Snipcart discovers your product details right from your HTML. You include &lt;code&gt;data-&lt;/code&gt; attributes defining things like a product's price and options and Snipcart handles the rest. And don't worry, Snipcart verifies all product details before accepting an order to make sure there wasn't any funny business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static e-commerce with Astro
&lt;/h2&gt;

&lt;p&gt;Enough already, lets get to the solution! Fair warning, this demo is a fairly direct port from &lt;a href="https://demo.kamfly.io"&gt;Kamfly's demo&lt;/a&gt;. That project was originally written in &lt;a href="https://11ty.dev"&gt;11ty&lt;/a&gt;, another static site generator that provides an excellent developer experience for fans of JavaScript.&lt;/p&gt;

&lt;p&gt;The UI design of the demo menu may look familiar to a few of you reading this. My first job out of college was as a Software Development Engineer in Test (SDET) at &lt;a href="https://microsoft.com"&gt;Microsoft&lt;/a&gt;. I worked on the UI team responsible for Windows Phone 7-10 and was always partial to the subway-inspired design!&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the menu data
&lt;/h3&gt;

&lt;p&gt;I didn't want to stray too far from the original 11ty project, and that meant storing all the menu data in local JSON files.&lt;/p&gt;

&lt;p&gt;You can see the full details &lt;a href="https://github.com/Navillus-BV/demo-astro-snipcart/blob/main/src/utils/loadMenu.js"&gt;in GitHub&lt;/a&gt;, but the basic solution here takes advantage of &lt;a href="https://vitejs.dev/guide/features.html#glob-import"&gt;glob imports&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../data/menus/*.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is a bit of import magic that allows you to use a &lt;a href="https://www.npmjs.com/package/glob"&gt;glob pattern&lt;/a&gt; to load multiple files at once.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;import.meta.glob&lt;/code&gt; the result is an object mapping from filename to an async function that loads the file. In case you want to load all the data immediately, &lt;code&gt;import.meta.globEager&lt;/code&gt; will be your best friend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the shopping cart button
&lt;/h3&gt;

&lt;p&gt;Snipcart makes this extremely simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header__cartbtn snipcart-checkout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header__cart"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/icons/cart.svg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"open cart"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"snipcart-items-count header__cartcount"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it! Snipcart checks your page for a specific classes. In this case &lt;code&gt;snipcart-checkout&lt;/code&gt; tells Snipcart this is the button that should open the shopping cart when clicked. The &lt;code&gt;.snipcart-items-count&lt;/code&gt; span is another special class telling Snipcart to update this span with the current number of items in the cart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding products to the cart
&lt;/h3&gt;

&lt;p&gt;Snipcart looks for another special class to discover products on your site, &lt;code&gt;snipcart-add-item&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button
    class="menuitem__cartbtn snipcart-add-item"
    data-item-id={item.slug}
    data-item-name={item.display_title || item.title}
    data-item-price={item.price}
    data-item-url={url}
    data-item-image={item.image}
    {...modifiersMap}
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of this should be pretty straight forward, we're adding &lt;code&gt;data-item&lt;/code&gt; properties to tell Snipcart what the product is. But what about that &lt;code&gt;modifiersMap&lt;/code&gt; thing?&lt;/p&gt;

&lt;h3&gt;
  
  
  Snipcart custom fields
&lt;/h3&gt;

&lt;p&gt;Snipcart allows you to add custom options to products, think dropdowns for &lt;code&gt;size: Medium&lt;/code&gt; or &lt;code&gt;color: Gray&lt;/code&gt;. To do this their API supports &lt;a href="https://docs.snipcart.com/v3/setup/products#custom-fields"&gt;custom fields&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The string formatting for custom fields can be a bit confusing at first glance, but it's hugely powerful. Not only can you tell Snipcart what options are available, you can even define extra charges for specific options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;data-item-custom1-name=&lt;/span&gt;&lt;span class="s"&gt;"Frame color"&lt;/span&gt;
  &lt;span class="na"&gt;data-item-custom1-options=&lt;/span&gt;&lt;span class="s"&gt;"Black|Brown[+100.00]|Gold[+300.00]"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, Snipcart would give the user a dropdown option for "Frame color". The color black is free, upgrading to brown adds $100 to the product's price and upgrading to gold adds $300.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding custom fields in Astro
&lt;/h4&gt;

&lt;p&gt;This one was a bit tricky to figure out at first, hopefully I'll help save you some time here.&lt;/p&gt;

&lt;p&gt;I realized pretty quickly that the &lt;code&gt;{...spread}&lt;/code&gt; operator was going to be my friend. Because &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt; allows JavaScript (and TypeScript!) inside the template's &lt;code&gt;---&lt;/code&gt; fence, it really is just a matter of massaging the product options into an object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;modifierMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data-item-custom1-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sauce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data-item-custom1-options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"None|BBQ|Buffalo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data-item-custom2-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Side"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data-item-custom2-options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fries|Onion Rings[+1.50]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll leave it to you to check the &lt;a href="https://github.com/Navillus-BV/demo-astro-snipcart/blob/main/src/components/MenuItem.astro"&gt;full implementation&lt;/a&gt; in GitHub, but once we have the map of option properties it really is as simple as &lt;code&gt;{...spread}&lt;/code&gt;ing those properties onto the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; in Astro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A huge thanks is in order here to both the &lt;a href="https://snipcart.com"&gt;Snipcart&lt;/a&gt; and &lt;a href="https://astro.blog"&gt;Astro&lt;/a&gt; teams here. I had the easy job of tying together two excellent projects to build a completely static restaurant ordering site!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>snipcart</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Astro + Foresty CMS</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Mon, 28 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/astro-foresty-cms-38lc</link>
      <guid>https://dev.to/navillusbv/astro-foresty-cms-38lc</guid>
      <description>&lt;p&gt;This is a follow-up post to our &lt;a href="https://dev.to/blog/astro-plus-netlify-cms"&gt;Astro + Netlify CMS&lt;/a&gt; demo - check that one out first if you haven't read it yet!&lt;/p&gt;





&lt;blockquote&gt;
&lt;p&gt;Our go-to content solutions:&lt;a href="https://twitter.com/NetlifyCMS?ref_src=twsrc%5Etfw"&gt;@NetlifyCMS&lt;/a&gt; for internal projects (when we just need it to work)&lt;a href="https://twitter.com/forestryio?ref_src=twsrc%5Etfw"&gt;@forestryio&lt;/a&gt; for client projects (when a clean, user friendly UI is a must)&lt;a href="https://twitter.com/fauna?ref_src=twsrc%5Etfw"&gt;@fauna&lt;/a&gt; (when a file based CMS just won't cut it)&lt;br&gt;&lt;br&gt;What tools are you always reaching for?&lt;/p&gt;— Navillus (&lt;a class="mentioned-user" href="https://dev.to/navillus_dev"&gt;@navillus_dev&lt;/a&gt;
) &lt;a href="https://twitter.com/navillus_dev/status/1406690186189328384?ref_src=twsrc%5Etfw"&gt;June 20, 2021&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;Every CMS has it's benefits and best uses, but it's rare that we have to reach outside these three tools. We've already tried out Netlify CMS with Astro and, not surprisingly given how closely the &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt; static site generator sticks to plain old HTML/CSS/JS, it was a breeze to get all setup. This begs the question, how does &lt;a href="https://forestry.io/"&gt;Forestry&lt;/a&gt; hold up to this brand new framework?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check out the &lt;a href="https://demo-astro-forestry.netlify.app"&gt;live demo&lt;/a&gt;, &lt;a href="https://github.com/Navillus-BV/demo-astro-forestry"&gt;GitHub repo&lt;/a&gt;, or jump straight to the &lt;a href="https://github.com/navillus-bv/demo-astro-forestry/compare/8b8ab5527738d0575a4aa7509ab5c4e605b64736...0aecbd42aaa1dc3bf21b0e0ad93e08fe47e4533b"&gt;diff&lt;/a&gt; comparing the demo to the original Astro &lt;a href="https://github.com/snowpackjs/astro/tree/main/examples/blog"&gt;blog example&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The power of Forestry
&lt;/h2&gt;

&lt;p&gt;Forestry CMS sits in a very special niche - a git-based CMS that is designed with non-technical users in mind. Being git-based, it allows for an extremely simple developer experience.&lt;/p&gt;

&lt;p&gt;If you're working on a Jamstack project with a statically built front end and expect most of the content updates to be done via the CMS rather than markdown directly, I can't recommend Forestry enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before you ask&lt;/strong&gt; - No, this isn't a paid post. Navillus aren't affiliated with Forestry, we're just big fans of the product.&lt;/p&gt;
&lt;h2&gt;
  
  
  What changes were needed
&lt;/h2&gt;

&lt;p&gt;I started this demo with Astro's &lt;a href="https://github.com/snowpackjs/astro/tree/main/examples/blog"&gt;blog example&lt;/a&gt;. Let's break down the changes that were required to setup the CMS.&lt;/p&gt;
&lt;h3&gt;
  
  
  Moving image assets to &lt;code&gt;/public/uploads&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This one is more personal preference than anything else. Many CMS tools drop all your image assets into the same location and let you visually pick images from a gallery.&lt;/p&gt;

&lt;p&gt;The only time I've really found it useful to separate image assets into different directories was to transform specific images depending on their use, and we won't be bothering with image optimization for this demo.&lt;/p&gt;

&lt;p&gt;Note that this also requires updating any image references from &lt;code&gt;/images&lt;/code&gt; to &lt;code&gt;/uploads&lt;/code&gt;. In our case, that meant updating &lt;code&gt;/src/pages/about.astro&lt;/code&gt; and each blog post in &lt;code&gt;/src/pages/posts&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Moving authors data to markdown
&lt;/h3&gt;

&lt;p&gt;The example repo includes a &lt;code&gt;/src/data/authors.json&lt;/code&gt; file which is a basic JSON object/map of the two demo authors. This structure doesn't make as much sense for a file-based CMS.&lt;/p&gt;

&lt;p&gt;Instead, let's store each author in a separate markdown file in the &lt;code&gt;/src/data/authors&lt;/code&gt; directory. Later we can point Forestry to that folder, define the property types available for each author, and allow CMS users to create new authors without touching JSON.&lt;/p&gt;

&lt;p&gt;While you're here, make sure each author's image property is pointing to the &lt;code&gt;/uploads&lt;/code&gt; directory instead of &lt;code&gt;/images&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Loading author information
&lt;/h3&gt;

&lt;p&gt;This really was the only tricky bit to work out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;authorData&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../data/authors.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few different pages and templates need to load the author data, and they all expected ot find a JSON map. Now that each author has a separate markdown file we need to fix how that data is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allAuthors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../data/authors/*.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;authorData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allAuthors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`src/data/authors/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md`&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's with the &lt;code&gt;src/data/authors/${next.slug}.md&lt;/code&gt; code? We'll be setting up Forestry soon, but one thing to note now is how Forestry handles content relationships by default.&lt;/p&gt;

&lt;p&gt;By the end of this post you'll be able to create authors in the CMS and link an author to each blog post. That's right, this git-based CMS handles relational data out of the box! (Sorry, no table joins, it's just a git repo!)&lt;/p&gt;

&lt;p&gt;Forestry references other CMS objects by absolute path, based on the projects root directory. In the case of &lt;code&gt;authorData&lt;/code&gt; above, we're mapping from each author's filepath to the author's data. There are other ways you could manage the data here, but for the demo this is easier since you won't need to update the pages templates otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the CMS
&lt;/h2&gt;

&lt;p&gt;This is the easy, and fun, part! Once you have the project building locally and pushed to GitHub, head over to &lt;a href="https://forestry.io"&gt;Forestry&lt;/a&gt; and follow their importing walk through.&lt;/p&gt;

&lt;p&gt;Forestry's repo onboarding process is really impressive. Once linked to your repo, Forestry walks you through the steps of defining your data types (in our case just Author and Post).&lt;/p&gt;

&lt;p&gt;I'll leave it to you to play around with Forestry's content model settings. Take a few minutes to poke around in the different options, especially for defining different property types and data validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus points&lt;/strong&gt; Forestry offers live previews that run your actual dev server and give previews of content updates before publishing. I've had luck running Astro with node v14 and out of the box Forestry only offers v12. They support custom docker images from DockerHub though, go nuts and setup your own preview server!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Keep an eye out for a part two of this Forestry demo! I want to revisit the best way to edit the home and about pages, or create new pages for that matter, right in the CMS.&lt;/p&gt;

&lt;p&gt;And yes, if the tweet at the top of this post didn't give it away, we'll be posting an Astro + &lt;a href="https://fauna.com/"&gt;FaunaDB&lt;/a&gt; demo soon enough!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>cms</category>
    </item>
    <item>
      <title>Class properties in Svelte</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Fri, 25 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/class-properties-in-svelte-3km6</link>
      <guid>https://dev.to/navillusbv/class-properties-in-svelte-3km6</guid>
      <description>&lt;p&gt;I've noticed an interesting pattern when a &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; developer gives &lt;a href="https://svelte.dev"&gt;Svelte&lt;/a&gt; a try - they almost immediately add a &lt;code&gt;className&lt;/code&gt; property to their first Svelte component.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, the power of dynamically adding classes to a component can save your butt. The term &lt;code&gt;className&lt;/code&gt; is a dodge in the JSX world though since &lt;code&gt;class&lt;/code&gt; is a reserved word in JavaScript. Here's a quick trick to make your Svelte components feel even more like plain old HTML with a proper &lt;code&gt;class&lt;/code&gt; property!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check out the &lt;a href="https://svelte.dev/repl/ee6a1591aa214368b56804241f0f4c6d?version=3.38.3"&gt;REPL&lt;/a&gt; example to see a working example.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the problem
&lt;/h2&gt;

&lt;p&gt;With Svelte, you may think it's as simple as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately you run smack into the same reserved word problem - Svelte treats the &lt;code&gt;script&lt;/code&gt; as regular JavaSript (or TypeScript) and won't allow a variable named &lt;code&gt;class&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The workaround
&lt;/h3&gt;

&lt;p&gt;If you're like me you probably hit this once, banged your head against the nearest wall a couple times, then moved on to the obvious fix of renaming the prop to &lt;code&gt;class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"{className}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing Svelte is so close to HTML that you can almost forget there's a framework at all...until you come across something like &lt;code&gt;&amp;lt;Button className="send-btn"/&amp;gt;&lt;/code&gt;. Those four extra characters stand out like a sour thumb, flashing a big neon sign to remind you this isn't actually HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  There's an easy fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"{className}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it! Internally the property is named &lt;code&gt;className&lt;/code&gt;, avoiding JavaScript's reserved word issue. Externally, though, the component has an optional property named &lt;code&gt;class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"send-btn"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doesn't that just feel right? No more JSX-like &lt;code&gt;className&lt;/code&gt; property screaming "is this React!?!"&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;React is a very powerful framework and web development wouldn't be where it is today without it. But when it comes to workarounds like renaming &lt;code&gt;class&lt;/code&gt; to &lt;code&gt;className&lt;/code&gt;, or camel casing CSS properties for that matter, it can feel freeing when you can move past those quirks and get back to the basics.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>react</category>
    </item>
    <item>
      <title>Astro + Netlify CMS</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Wed, 23 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/astro-netlify-cms-4kk</link>
      <guid>https://dev.to/navillusbv/astro-netlify-cms-4kk</guid>
      <description>&lt;p&gt;I like markdown as much as the next developer, but it's easy to forget how convenient a CMS can be. For personal projects or internal tools, though, I can't justify reaching for a full headless CMS setup. Configuration takes time and, more importantly, for Jamstack sites it can be a huge time saver to have the entire CMS alongside the source code right in &lt;code&gt;git&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check the &lt;a href="https://demo-astro-netlify-cms.netlify.app/"&gt;live demo&lt;/a&gt; or dive right into the code on &lt;a href="https://github.com/Navillus-BV/demo-astro-netlify-cms"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The power of a git-based CMS
&lt;/h2&gt;

&lt;p&gt;We're huge fans of the git-based CMS idea at Navillus. When the entire site is built to be deployed as a static site it really doesn't make much sense to need to pull from a remote server to load content, and you're already working in &lt;code&gt;git&lt;/code&gt;!&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;Our go-to content solutions:&lt;a href="https://twitter.com/NetlifyCMS?ref_src=twsrc%5Etfw"&gt;@NetlifyCMS&lt;/a&gt; for internal projects (when we just need it to work)&lt;a href="https://twitter.com/forestryio?ref_src=twsrc%5Etfw"&gt;@forestryio&lt;/a&gt; for client projects (when a clean, user friendly UI is a must)&lt;a href="https://twitter.com/fauna?ref_src=twsrc%5Etfw"&gt;@fauna&lt;/a&gt; (when a file based CMS just won't cut it)&lt;br&gt;&lt;br&gt;What tools are you always reaching for?&lt;/p&gt;— Navillus (&lt;a class="mentioned-user" href="https://dev.to/navillus_dev"&gt;@navillus_dev&lt;/a&gt;
) &lt;a href="https://twitter.com/navillus_dev/status/1406690186189328384?ref_src=twsrc%5Etfw"&gt;June 20, 2021&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;For those that follow us on &lt;a href="https://twitter.com/navillus_dev"&gt;Twitter&lt;/a&gt;, today's blog post won't be much of a surprise. Netlify CMS is the first content management tool we reach for on side projects and internal tools. It's dead simple to setup and deploy, and is deployed alongside our site as static HTML. And before you as, yes we are loading the admin panel's JS bundle from a CDN but you can actually install that via NPM if you prefer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Before we begin
&lt;/h2&gt;

&lt;p&gt;This demo is based on the excellent &lt;a href="https://github.com/danurbanowicz/eleventy-netlify-boilerplate"&gt;eleventy-netlify-boilerplate&lt;/a&gt; demo. If you're interested in &lt;a href="https://11ty.dev"&gt;11ty&lt;/a&gt; as well, I strongly recommend you take a look at that repo to learn best practices when setting up an 11ty project!&lt;/p&gt;
&lt;h2&gt;
  
  
  The basic setup
&lt;/h2&gt;

&lt;p&gt;Our goal today is to highlight the Astro-specific details when integrating with Netlify CMS, so I won't be diving too far into the initial setup. Check out &lt;a href="https://www.netlifycms.org/docs/add-to-your-site/"&gt;Netlify CMS's&lt;/a&gt; excellent docs for adding the CMS to your own site for a quick rundown.&lt;/p&gt;

&lt;p&gt;For this demo, I decided to load the &lt;code&gt;netlify-cms&lt;/code&gt; library from CDN, but as mentioned &lt;a href="https://www.netlifycms.org/docs/add-to-your-site/#installing-with-npm"&gt;in the docs&lt;/a&gt;, you can install from NPM instead. In that case, &lt;a href="https://www.snowpack.dev/"&gt;Snowpack&lt;/a&gt; will handle bundling the JS in production builds.&lt;/p&gt;

&lt;p&gt;When including &lt;code&gt;/admin/index.html&lt;/code&gt; and &lt;code&gt;/admin/config.yml&lt;/code&gt;, you can simply copy those files from the docs to your Astro project's &lt;code&gt;/public&lt;/code&gt; folder. Astro includes everything in the &lt;code&gt;/public&lt;/code&gt; directory as static assets, for example your &lt;code&gt;/public/admin/index.html&lt;/code&gt; file will be available when navigating to &lt;code&gt;yoursite.com/admin&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo blog posts
&lt;/h2&gt;

&lt;p&gt;First thing's first, lets setup support for blog posts.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring blog posts in the CMS
&lt;/h3&gt;

&lt;p&gt;Once you have the CMS and Netlify Identity all setup, it's time to start adding content. If you take a look at our demo repo, you'll see that all of the blog posts are saved to the &lt;a href="https://github.com/Navillus-BV/demo-astro-netlify-cms/tree/main/src/pages/posts"&gt;/src/pages/posts&lt;/a&gt; directory.&lt;/p&gt;

&lt;p&gt;For Netlify CMS, The key is to make sure that your &lt;code&gt;config.yml&lt;/code&gt; is pointing to the correct folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Our blog posts&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blog"&lt;/span&gt; &lt;span class="c1"&gt;# Used in routes, e.g., /admin/collections/blog&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Post"&lt;/span&gt; &lt;span class="c1"&gt;# Used in the UI&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/pages/posts"&lt;/span&gt; &lt;span class="c1"&gt;# The path to the folder where the documents are stored&lt;/span&gt;
    &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Allow users to create new documents in this collection&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{slug}}"&lt;/span&gt; &lt;span class="c1"&gt;# Filename template, e.g., YYYY-MM-DD-title.md&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# The fields for each document, usually in front matter&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Date"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;datetime"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Author"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Anonymous"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summary"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tags"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;markdown"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this excerpt from the demo's &lt;code&gt;config.yml&lt;/code&gt;, note that &lt;code&gt;folder&lt;/code&gt; is pointing to the correct directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading and rendering in Astro
&lt;/h3&gt;

&lt;p&gt;Loading local data is handled with the &lt;a href="https://github.com/snowpackjs/astro#-fetching-data"&gt;Astro.fetchContent&lt;/a&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createCollection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/** Load posts, sort newest -&amp;gt; oldest */&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./posts/*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allPosts&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="cm"&gt;/** Set page size */&lt;/span&gt;
        &lt;span class="na"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's really all there is to it! The &lt;code&gt;fetchContent&lt;/code&gt; API takes care of loading all matching markdown files. I left out RSS feed support here for brevity, but you can find that in the demo repo &lt;a href="https://github.com/Navillus-BV/demo-astro-netlify-cms/blob/main/src/pages/%24blog.astro"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Layout title={title} description={description}&amp;gt;
    &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;

    {collection.data.map((post) =&amp;gt; (
        &amp;lt;PostPreview post={post} /&amp;gt;
    ))}
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the &lt;code&gt;$blog.astro&lt;/code&gt; template is taking the data loaded above and rendering a list of post previews. If you have experience with React (or JSX) this will feel very familiar. Brackets &lt;code&gt;{}&lt;/code&gt; are used to escape plain old JS into the template, mapping over the posts loaded in &lt;code&gt;data()&lt;/code&gt; and passing the data of to the &lt;code&gt;PostPreview&lt;/code&gt; component.&lt;/p&gt;

&lt;h4&gt;
  
  
  What about individual blog posts?
&lt;/h4&gt;

&lt;p&gt;Take a look at one of the sample &lt;a href="https://github.com/Navillus-BV/demo-astro-netlify-cms/blob/main/src/pages/posts/firstpost.md"&gt;blog posts&lt;/a&gt; in the demo repo. It defines the template used to render a blog post in frontmatter, just like you may have used in &lt;a href="https://11ty.dev"&gt;11ty&lt;/a&gt;, &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, or really any other static site generator out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat, Astro is in beta!
&lt;/h2&gt;

&lt;p&gt;Astro is still in beta and one of the big updates coming down the pipe is an update to &lt;a href="https://github.com/snowpackjs/astro/issues/80"&gt;dynamic routing&lt;/a&gt;. We'll skip past the routing setup for now as that may very well change in the near future, but feel free to poke around in the &lt;a href="https://github.com/Navillus-BV/demo-astro-netlify-cms"&gt;demo repo&lt;/a&gt; or ask us questions on &lt;a href="https://twitter.com/navillus_dev"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I won't go into detail here on how &lt;code&gt;/author/:id&lt;/code&gt; or &lt;code&gt;/tags/:tag&lt;/code&gt; routes for now, but keep an eye out for a follow-up blog post once the routing APIs are finalized!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>cms</category>
    </item>
    <item>
      <title>Keeping it simple with Astro</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Mon, 14 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/keeping-it-simple-with-astro-4734</link>
      <guid>https://dev.to/navillusbv/keeping-it-simple-with-astro-4734</guid>
      <description>&lt;p&gt;Frontend frameworks have taken over much of the web, but the question remains - do we really need all that JavaScript in the browser?&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it simple
&lt;/h2&gt;

&lt;p&gt;Client-side rendering can be an extremely powerful tool, and in some cases can solve problems that are nearly impossible to solve strictly with server rendering. A vast majority of the web is pretty simple though - mostly static content with a bit of interactivity sprinkled in.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://navillus.dev"&gt;Navillus&lt;/a&gt; is a perfect example. It has a mobile menu taking advantage of Svelte's excellent &lt;a href="https://svelte.dev/docs#svelte_transition"&gt;built-in transition&lt;/a&gt;, and we recently added in a dark mode toggle. That's it for interactions on our site though, the rest is entirely static.&lt;/p&gt;

&lt;p&gt;&lt;a href="/posts/assets/2021-06-14-navillus-dev-tools.png" class="article-body-image-wrapper"&gt;&lt;img src="/posts/assets/2021-06-14-navillus-dev-tools.png" alt="Screenshot of Navillus devtools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a screenshot of devtools with our homepage loaded. A grand total of ~78KB transferred, including all the HTML, CSS, JavaScript, images, and fonts. Don't get me wrong, there's more to web design and development than just minimizing bundle size, but performance matters and small wins can add up quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the trick?
&lt;/h3&gt;

&lt;p&gt;When designing our site we had performance in mind from the beginning. You may notice that we purposely don't have any images on our homepage, and the only icons used are actually taking advantage of the &lt;a href="https://css-tricks.com/svg-sprites-use-better-icon-fonts/"&gt;SVG sprites&lt;/a&gt; technique to load all the icons in a single file.&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;Just finished rebuilding &lt;a href="https://t.co/UcT54hdIY5"&gt;&lt;/a&gt;&lt;a href="https://t.co/UcT54hdIY5"&gt;https://t.co/UcT54hdIY5&lt;/a&gt; with &lt;a href="https://twitter.com/astrodotbuild?ref_src=twsrc%5Etfw"&gt;@astrodotbuild&lt;/a&gt;&lt;br&gt;&lt;br&gt;Progressively enhanced with &lt;a href="https://twitter.com/sveltejs?ref_src=twsrc%5Etfw"&gt;@sveltejs&lt;/a&gt; and clocking in at a whopping 12.5KB of total JS. Really digging how easy its becoming to build tiny sites these days&lt;/p&gt;— Navillus (&lt;a class="mentioned-user" href="https://dev.to/navillus_dev"&gt;@navillus_dev&lt;/a&gt;
) &lt;a href="https://twitter.com/navillus_dev/status/1403481307657691139?ref_src=twsrc%5Etfw"&gt;June 11, 2021&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;As of last week, though, we officially swapped our site over to &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;. Why go through all that effort to trust our site with an early beta of a totally new project? Well, why not!&lt;/p&gt;

&lt;p&gt;We didn't actually plan to go live with an Astro version of our site, expecting to kick the tires and keep an eye on the project. The build was surprisingly straightforward though, and the results don't lie.&lt;/p&gt;
&lt;h2&gt;
  
  
  Shipping just enough JavaScript
&lt;/h2&gt;

&lt;p&gt;The main bit of JavaScript on our site is the mobile menu - we hadn't actually added the dark theme toggle yet when first starting the Astro project. We already posted about &lt;a href="https://dev.to/blog/progressive-enhancement"&gt;how we built&lt;/a&gt; our progressively enhanced menu, but the key is that we wanted to use Svelte to easily work with or without JavaScript. If the JS loads, we pull in Svelte's &lt;code&gt;slide&lt;/code&gt; transition to nicely animate the menu in and out.&lt;/p&gt;

&lt;p&gt;With Astro, we can build 98% of our site with little more than HTML templates and markdown while adding in a tiny amount of JavaScript to improve the experience. No client side routing, no frontend framework dynamically rendering the page, no hydration issues or content flickers.&lt;/p&gt;

&lt;p&gt;Note: Astro isn't opinionated about what component library you prefer. They shipped with Vue, React, Svelte, and Preact out of the box and more are being added as you read this.&lt;/p&gt;
&lt;h2&gt;
  
  
  Speaking of hydration
&lt;/h2&gt;

&lt;p&gt;One of the most interesting ideas about Astro is &lt;a href="https://github.com/snowpackjs/astro#-partial-hydration"&gt;partial hydration&lt;/a&gt;. By default a component's JavaScript won't even load in the browser, it will only be used on the server. That's not always enough though, and Astro gives you a few options for how to load it on the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Component:load /&amp;gt;
&amp;lt;Component:idle /&amp;gt;
&amp;lt;Component:visible /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Say it's a component that really needs to be available as soon as possible, just tack &lt;code&gt;:load&lt;/code&gt; onto the component and it will be loaded on the client when the page loads.&lt;/p&gt;

&lt;p&gt;Maybe the component is common but not needed immediately, adding &lt;code&gt;:idle&lt;/code&gt; instead will tell Astro to wait for the browser to initially load the page and go idle before pulling down the component, boosting performance on initial load.&lt;/p&gt;

&lt;p&gt;They have you covered for components further down the page too, &lt;code&gt;:visible&lt;/code&gt; will take advantage of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"&gt;IntersectionObserver API&lt;/a&gt; to only load the component if a user scrolls down to it. This might be useful for a contact form at the bottom of a marketing page where you need custom JS validation logic but don't want to risk any performance hiccups when every conversion matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coming soon to an Astro near you
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Component:media ="(min-width: 40em)"&amp;gt;&amp;lt;/Component:media&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is only a proposed feature still open on &lt;a href="https://github.com/snowpackjs/astro/issues/396"&gt;Github&lt;/a&gt; and the syntax may very likely change, but the concept here will be a huge win.&lt;/p&gt;

&lt;p&gt;Take our mobile menu again. We're using &lt;code&gt;:idle&lt;/code&gt; to allow our whole page to load before enhancing the menu, but on desktop that menu is never even used. With &lt;code&gt;:media&lt;/code&gt; or similar we could make sure that the menu's JS is only loaded on mobile viewports, why even both loading it?&lt;/p&gt;

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

&lt;p&gt;Keep your eye on &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;, follow their &lt;a href="https://github.com/snowpackjs/astro"&gt;Github repo&lt;/a&gt; (or star it if you prefer), and check out the community Discord linked on their homepage to give feedback and share your ideas for the future of Astro!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>svelte</category>
      <category>performance</category>
    </item>
    <item>
      <title>The power of Svelte actions</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Thu, 03 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/the-power-of-svelte-actions-19k7</link>
      <guid>https://dev.to/navillusbv/the-power-of-svelte-actions-19k7</guid>
      <description>&lt;p&gt;Svelte does an excellent job of blurring the lines between frontend frameworks and plain old HTML/JS. Leaning on Svelte's &lt;a href="https://svelte.dev/docs#2_Assignments_are_reactive"&gt;reactivity model&lt;/a&gt;, &lt;a href="https://svelte.dev/docs#bind_element_property"&gt;property binding&lt;/a&gt;, and &lt;a href="https://svelte.dev/docs#on_element_event"&gt;event directives&lt;/a&gt; will get you 99% of the way to a finished app, but every once in a while you just really need to work directly with the &lt;code&gt;HTMLElement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This can be done in a one-off way with the &lt;code&gt;bind:this&lt;/code&gt; approach to getting a local reference to a DOM element, but there's a better way. The &lt;code&gt;use:&lt;/code&gt; directive allows for reusable logic to be pulled out of the component itself.&lt;/p&gt;

&lt;p&gt;Let's take a look at a really common use for this - listening for clicks/taps outside an area of the UI. I used this recently for &lt;a href="https://kamfly.io"&gt;Kamfly's&lt;/a&gt; modals, instead of adding a one-off click listener in the &lt;code&gt;Modal&lt;/code&gt; component I created a &lt;code&gt;clickOutside&lt;/code&gt; action that can be reused elsewhere. This can also help make testing a whole lot easier, testing the async nature of a full blown Modal component can be tricky, testing just the action itself is much less complicated!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check out the final example in this &lt;a href="https://svelte.dev/repl/c44a10b2b200447ab733da39a448111d?version=3.38.2"&gt;Live REPL demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Svelte actions
&lt;/h2&gt;

&lt;p&gt;Svelte's official tutorial has an excellent walk through of &lt;a href="https://svelte.dev/tutorial/actions"&gt;what actions are&lt;/a&gt;, but basically you can think of actions as lifecycle hooks for DOM elements themselves. An action works much like &lt;code&gt;onMount&lt;/code&gt; or &lt;code&gt;onDestroy&lt;/code&gt;, but actions are tied to DOM elements themselves rather than entire Svelte components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why bother with actions?
&lt;/h3&gt;

&lt;p&gt;It's easier to reach for &lt;code&gt;bind:this&lt;/code&gt; when the component you're working on needs to interact with a DOM element. I won't jump into the DRY design principles debate, instead just look at the testing challenges.&lt;/p&gt;

&lt;p&gt;Many projects end up needing a modal at some point, and they're almost always ignored in automated testing. When the component itself is responsible for hiding/showing the modal, managing a stack of multiple open modals, etc. the logic gets tricky fast.&lt;/p&gt;

&lt;p&gt;Testing asynchronous behavior like animations or modals hiding/showing based on user input can be a chore. What if you could move some of that logic out to a reusable function?&lt;/p&gt;

&lt;h3&gt;
  
  
  Anatomy of an action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's really all there is to it! An action in Svelte takes in the DOM element and (optionally) user defined parameters. The &lt;code&gt;action&lt;/code&gt; function can run initialization logic on the element before returning &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;destroy&lt;/code&gt; handlers (both optional).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;use:&lt;/code&gt; clickOutside
&lt;/h2&gt;

&lt;p&gt;Let's look at a really common UX pattern - a modal is open on screen and you want it to hide when the user clicks/taps outside the modal (often on some kind of grayed out background).&lt;/p&gt;

&lt;p&gt;This isn't a big deal to do in the Modal component itself - add an extra DOM element for the grayed out background and add a click handler to it. But what if you want a similar interaction for your mobile menu? That component is almost certainly different, if you copy the code over now you have duplicated JS in your site &lt;strong&gt;and&lt;/strong&gt; you need complicated test coverage testing the close functionality for both components.&lt;/p&gt;

&lt;h3&gt;
  
  
  The clickOutside action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onClickOutside&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClickOutside&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a few things going on here, let's break that down.&lt;/p&gt;

&lt;p&gt;First, the action is taking in optional parameters (&lt;code&gt;_options&lt;/code&gt;). The action expects &lt;code&gt;options.onClickOutside&lt;/code&gt; to be a callback function, if one wasn't provided it's defaulted to a noop function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;detect&lt;/code&gt; function does the real work of checking to see if a click event on the page was inside the original DOM element. The event listener is using &lt;code&gt;passive&lt;/code&gt; to avoid scrolling performance issues, and &lt;code&gt;capture&lt;/code&gt; to make sure that all clicks bubble up to the listener.&lt;/p&gt;

&lt;p&gt;Finally, the action returns a &lt;code&gt;destroy&lt;/code&gt; callback that will clean up the event listener when the element is being removed from the DOM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allow components to whitelist elements to ignore
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;onClickOutside&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSameNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClickOutside&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/** Same as above */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A little extra functionality here allows components to pass in other DOM elements that should be considered as "inside" the action (rather than "outside").&lt;/p&gt;

&lt;h2&gt;
  
  
  How to test it
&lt;/h2&gt;

&lt;p&gt;UI testing will be a topic for another day, but you can see how much easier this will be to test. Instead of having to write tests that can reliably wait for a Modal component to animate into view before testing the click away scenario, tests could work more closely to the REPL demo (below). DOM elements for a test wouldn't need to animate at all, they just need to accept a &lt;code&gt;click&lt;/code&gt; event so the test can count how many times the &lt;code&gt;onClickOutside&lt;/code&gt; handler was called!&lt;/p&gt;

&lt;h2&gt;
  
  
  Live REPL demo
&lt;/h2&gt;

&lt;p&gt;That's all, folks! Check out the &lt;a href="https://svelte.dev/repl/c44a10b2b200447ab733da39a448111d?version=3.38.2"&gt;Live REPL demo&lt;/a&gt; for a working example.&lt;/p&gt;

</description>
      <category>svelte</category>
    </item>
    <item>
      <title>Introducing chisel.css</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/introducing-chisel-css-bkl</link>
      <guid>https://dev.to/navillusbv/introducing-chisel-css-bkl</guid>
      <description>&lt;p&gt;Have you ever really tried rendering some HTML without any CSS? It's a hot mess! Modern browsers are &lt;a href="https://www.google.com/chromebook/chrome-os/"&gt;operating systems&lt;/a&gt; unto themselves these days - I don't envy anyone who has to sift through the complexity to add even a basic feature to a browser these days. But damn do I wish they would add spend a couple days to ship some decent looking default styles for HTML5 elements!&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter chisel.css
&lt;/h2&gt;

&lt;p&gt;Yes, it's another CSS framework. No, we don't want to see it being used for the next 10 years. If we have our way, &lt;code&gt;chisel.css&lt;/code&gt; will make clear just how easy it could be to have modern designs with out-of-the-box HTML elements.&lt;/p&gt;

&lt;p&gt;We've included the most common browser resets, similar to &lt;a href="https://github.com/necolas/normalize.css"&gt;normalize.css&lt;/a&gt; or &lt;a href="https://csstools.github.io/sanitize.css/"&gt;sanitize.css&lt;/a&gt;, along with modern styling based on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS custom properties&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the name?
&lt;/h2&gt;

&lt;p&gt;Ask any woodworker that's dabbled in hand tools and fine joinery what tool they'd keep if they could have only one, it'd be the chisel. Most people never even think about it, but when it comes down to it almost every tool used on wood is basically just a chisel anyway.&lt;/p&gt;

&lt;p&gt;Take a close look at a saw (be careful!). See all those tiny chisels we call saw teeth? Now look at a drill bit - yep, it's pretty much a chisel blade twisted around a stick.&lt;/p&gt;

&lt;p&gt;We realized we kept starting with the same basic CSS resets and element styles for virtually every Navillus project, after pasting it a half dozen times we figured it was time to standardize. The project quickly took aim at building one CSS framework that can be a complete reset for both browser vendor issues and horribly styled HTML elements.&lt;/p&gt;

&lt;p&gt;Change a few variables here and there, but the goal is for chisel to help every site look good without immediately, ready for your throw in your custom CSS (or wrap a chisel around a stick if you still can't find that damn 3/8" drill bit).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href="https://navillus-bv.github.io/chisel/"&gt;docs&lt;/a&gt; for full details, but it really is as simple as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-s&lt;/span&gt; chisel.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or from a CDN&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/chisel.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minified and compressed the entire bundle is ~3.2KB, with full support for dark mode and basic app theming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eating our own dog food
&lt;/h2&gt;

&lt;p&gt;We recently updated &lt;a href="https://navillus.dev"&gt;navillus.dev&lt;/a&gt; to use &lt;code&gt;chisel&lt;/code&gt; as a basis for styling, you can see the complete &lt;a href="https://github.com/Navillus-BV/navillus-dev/pull/11/files?file-filters%5B%5D=.postcss&amp;amp;file-filters%5B%5D=.svelte"&gt;pull request&lt;/a&gt; on GitHub. I'm always a fan of seeing a pull request full of red, gotta love code cleanup!&lt;/p&gt;

&lt;p&gt;Granted we're a bit biased here, most of the common CSS we copy into every Navillus sites made it's way into chisel. The power of CSS variables lets you make some pretty drastic changes though, with very little effort.&lt;/p&gt;

&lt;p&gt;Want a different primary color? No problem, change &lt;code&gt;--chisel-primary&lt;/code&gt; and you're all set. Prefer a different &lt;a href="https://type-scale.com/"&gt;type scale&lt;/a&gt;? That's simple too, check the docs for our variables like &lt;code&gt;--chisel-h1&lt;/code&gt; or &lt;code&gt;--chisel-p&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark theme
&lt;/h2&gt;

&lt;p&gt;Dark theme support is one that gave us more headaches than expected. We wanted chisel to support both browser's native &lt;code&gt;prefers-color-scheme&lt;/code&gt; as well as a fallback option. We landed on a custom HTML data property, &lt;code&gt;data-chisel-theme&lt;/code&gt;. In the future we plan to ship predefined color palettes that will also take advantage of the HTML &lt;code&gt;dataset&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;So what went wrong? If you take a look at v0.4.0, we added custom CSS properties for component-level styling similar to &lt;code&gt;--chisel-button-bg&lt;/code&gt;. This worked great for making it simple to custom style buttons, say if you want a button variant like &lt;code&gt;.button-hollow&lt;/code&gt;. Each component property defaulted to one of the main color palette styles, a la &lt;code&gt;--chisel-button-bg: var(--chisel-primary)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  There in lies the problem.
&lt;/h3&gt;

&lt;p&gt;At least on some browsers, CSS variable scope isn't always what you'd expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-chisel-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;---chisel-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#002244&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'd think this would also update any CSS properties referencing this variable. Nope! If &lt;code&gt;--chisel-button-bg&lt;/code&gt; is only defined on the &lt;code&gt;:root&lt;/code&gt; scope it isn't necessarily going to be updated to the new color code. This seems like a bug to me, and I have it on my list to dig through the spec to see if that's actually expected behavior, but in the meantime we will avoid it.&lt;/p&gt;

&lt;p&gt;Yes, we could use component variables if we redefined &lt;code&gt;--chisel-button-bg&lt;/code&gt; in the &lt;code&gt;dark&lt;/code&gt; theme selector as well. That'd lead to way to much bloat and extra CSS though, it wasn't worth the extra KB.&lt;/p&gt;

&lt;h2&gt;
  
  
  WIP
&lt;/h2&gt;

&lt;p&gt;Chisel is still very much a work in progress. Head over to the &lt;a href="http://github.com/navillus-bv/chisel"&gt;GitHub repo&lt;/a&gt; to file bugs, request new features, and star the project to follow the latest updates!&lt;/p&gt;

</description>
      <category>intro</category>
      <category>css</category>
    </item>
    <item>
      <title>Including json+ld structured data in Svelte</title>
      <dc:creator>Navillus</dc:creator>
      <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/navillusbv/including-json-ld-structured-data-in-svelte-19kc</link>
      <guid>https://dev.to/navillusbv/including-json-ld-structured-data-in-svelte-19kc</guid>
      <description>&lt;p&gt;Ever wonder how Google search results include shopping results for products, or location results for local businesses? Their search bot is constantly being improved to better find that kind of content on your site, but ultimately if you care at all about SEO on your site it's important to include &lt;a href="https://schema.org/"&gt;structured data&lt;/a&gt; on the page.&lt;/p&gt;

&lt;p&gt;Structured data allows you to give search bots scrubbing your page a helping hand by adding metadata that describes your &lt;a href="https://schema.org/Organization"&gt;business&lt;/a&gt;, &lt;a href="https://schema.org/Product"&gt;product&lt;/a&gt;, or even your &lt;a href="https://schema.org/BlogPosting"&gt;blog posts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://json-ld.org/"&gt;JSON+LD&lt;/a&gt; is a lightweight specification for including structured data as a simple JSON object, something we're all comfortable with in the frontend world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr;&lt;/strong&gt; Check out this &lt;a href="https://svelte.dev/repl/3382db29fc864d60b0a4ca47b3707a95?version=3.38.2"&gt;Svelte REPL&lt;/a&gt; example for a working example in JS. Keep reading to get full TypeScript validation for your Schema objects!&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON+LD by example
&lt;/h2&gt;

&lt;p&gt;Let's take a look at a basic example first. I pulled this straight from the &lt;a href="https://json-ld.org/playground/"&gt;JSON-LD Playground&lt;/a&gt; - it's an excellent reference for quickly messing around with the different schema and data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://schema.org/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Professor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"telephone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(425) 123-4567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://www.janedoe.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing crazy going on there (yet). When a search bot comes by to index a page with this JSON, it's guaranteed to find Professor Jane Doe's contact information. This contact information is probably included elsewhere on the page, say in a sidebar or page footer, but chances are the way the HTML is setup could prevent the search from connecting the link from a phone number to the person's name.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you include it on a page?
&lt;/h3&gt;

&lt;p&gt;This is where JSON+LD really stands out compared to other structured data formats that require special HTML attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/ld+json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://schema.org/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Person&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jobTitle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Professor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;telephone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(425) 123-4567&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://www.janedoe.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yep, that's really all there is to it. Wrap the JSON object in a &lt;code&gt;script&lt;/code&gt; tag, mark it as a type of &lt;code&gt;application/ld+json&lt;/code&gt; and you're all set. There's way more to the different kinds of schemas you may want to include - check out the &lt;a href="https://json-ld.org/learn.html"&gt;JSON-LD docs&lt;/a&gt; for a much better walk through of all the details than I would ever fit into a single blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON+LD in Svelte
&lt;/h2&gt;

&lt;p&gt;The beauty of &lt;a href="https://svelte.dev"&gt;Svelte&lt;/a&gt; is how closely it follows standard web technologies. Components are written in a single file with a &lt;code&gt;script&lt;/code&gt; tag for all the JavaScript, &lt;code&gt;style&lt;/code&gt; tag for the component's CSS, and one or more HTML elements for the actual DOM elements.&lt;/p&gt;

&lt;p&gt;While that makes authoring Svelte components a nearly frictionless experience for web developers, it can throw a wrench in the JSON+LD department. Tooling built for Svelte like the &lt;a href="https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode"&gt;VS Code plugin&lt;/a&gt;, and even the Svelte compiler itself, can cause headaches if you try to write an &lt;code&gt;application/ld+json&lt;/code&gt; script in your components.&lt;/p&gt;

&lt;p&gt;This may very well get cleaned up in the future, but for now you'll likely run into warnings and errors related to having more than one &lt;code&gt;script&lt;/code&gt; tag in a component. A Google search will lead you down a rabbit hole of different combinations of Svelte's &lt;a href="https://svelte.dev/docs#html"&gt;@html&lt;/a&gt; expressions and string literals to get it to compile.&lt;/p&gt;

&lt;p&gt;There's a much better way to do this though, and even better it can be combined with TypeScript to add extra type validations for your JSON schema objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serialize your JSON+LD to a script
&lt;/h3&gt;

&lt;p&gt;The first part of the magic trick is getting around issues related to parsing extra &lt;code&gt;script&lt;/code&gt; tags in your Svelte component. This is where many will reach for some combination of &lt;code&gt;{$html}&lt;/code&gt; and string literals.&lt;/p&gt;

&lt;p&gt;I prefer to move this out of the Svelte component all together. With the logic in a plain old JavaScript file it can be unit tested and will never run into issues with tools trying to parse &lt;code&gt;.svelte&lt;/code&gt; files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;serializeSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;script type="application/ld+json"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/script&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's not much magic going on here, the function just takes in a JavaScript object and spits out the &lt;code&gt;ld+json&lt;/code&gt; script tag for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type checking with TypeScript
&lt;/h3&gt;

&lt;p&gt;We'll lean on the &lt;a href="https://github.com/google/schema-dts"&gt;schema-dts&lt;/a&gt; package for full &lt;a href="https://schema.org/"&gt;Schema.org&lt;/a&gt; type definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WithContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;schema-dts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;WithContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Thing&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;serializeSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;script type="application/ld+json"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/script&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;schema-dts&lt;/code&gt; defines types for all of the different schema objects, see below for a more detailed example with an &lt;code&gt;Organization&lt;/code&gt; object. This is a huge win, it's easy to accidentally structure the JSON wrong or have a typo in one of the property names. Setting it up to use TypeScript definitions we can make sure that our JSON objects are validated at build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining our Schema.org objects
&lt;/h3&gt;

&lt;p&gt;Here's the &lt;code&gt;Organization&lt;/code&gt; object used by this very site.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;`$data/site.json`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;organizationSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WithContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Organization&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://schema.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Organization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;#organization`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sameAs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`https://twitter.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;social&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/favicon.svg`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Data specific to our site is pulled in from a local JSON file. This could be data exposed through a Git-based CMS like &lt;a href="https://forestry.io"&gt;Forestry&lt;/a&gt; or pulled down from a headless CMS like &lt;a href="https://sanity.io"&gt;Sanity&lt;/a&gt;. The important thing here is that our &lt;code&gt;Organization&lt;/code&gt; object is defined in TypeScript, verified at build-time, and can be unit tested if you want to make sure the &lt;code&gt;site.json&lt;/code&gt; data is hooked up properly.&lt;/p&gt;

&lt;p&gt;What is &lt;code&gt;WithContext&lt;/code&gt; doing? That's a clever setup from &lt;code&gt;schema-dts&lt;/code&gt;, the top-level JSON+LD object should have a &lt;code&gt;@context&lt;/code&gt; property. You can nest objects though, and they even have support for using an &lt;a href="https://json-ld.org/spec/latest/json-ld/#dfn-graph-objects"&gt;object graph&lt;/a&gt; format for multiple objects. Any nested objects, or every object in the graph, doesn't need &lt;code&gt;@context&lt;/code&gt;. &lt;code&gt;WithContext&lt;/code&gt; is a TypeScript wrapper for another type, in the example above I could have removed &lt;code&gt;@context&lt;/code&gt; from the object and it would be a valid &lt;code&gt;Organization&lt;/code&gt; type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing into Svelte
&lt;/h3&gt;

&lt;p&gt;Finally, let's add the JSON+LD into the DOM. Most of our projects end up with a &lt;code&gt;LDTag.svelte&lt;/code&gt; component similar to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;serializeSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$utils/json-ld&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$utils/json-ld&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;svelte:head&amp;gt;&lt;/span&gt;
  {@html serializeSchema(schema)}
&lt;span class="nt"&gt;&amp;lt;/svelte:head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple Svelte components on a page can inject their own &lt;code&gt;application/ld+json&lt;/code&gt; script into the document's head. This works great with &lt;a href="https://kit.svelte.dev"&gt;SvelteKit&lt;/a&gt; too, a &lt;a href="https://kit.svelte.dev/docs#layouts"&gt;layout&lt;/a&gt; or &lt;a href="https://kit.svelte.dev/docs#routing-pages"&gt;route&lt;/a&gt; component could inject page-level schemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working REPL example
&lt;/h2&gt;

&lt;p&gt;Take a look at our &lt;a href="https://svelte.dev/repl/3382db29fc864d60b0a4ca47b3707a95?version=3.38.2"&gt;Svelte REPL&lt;/a&gt; example to see a full working JavaScript example.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>seo</category>
    </item>
  </channel>
</rss>
