{"id":297067,"date":"2019-10-23T07:22:14","date_gmt":"2019-10-23T14:22:14","guid":{"rendered":"https:\/\/css-tricks.com\/?p=297067"},"modified":"2019-10-23T07:22:14","modified_gmt":"2019-10-23T14:22:14","slug":"what-i-like-about-writing-styles-with-svelte","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/what-i-like-about-writing-styles-with-svelte\/","title":{"rendered":"What I Like About Writing Styles with Svelte"},"content":{"rendered":"

There\u2019s been a lot of well-deserved hype around Svelte recently, with the project accumulating over 24,000 GitHub stars<\/a>. Arguably the simplest JavaScript framework out there, Svelte was written by Rich Harris, the developer behind Rollup<\/a>. There\u2019s a lot to like about Svelte (performance, built-in state management, writing proper markup rather than JSX), but the big draw for me has been its approach to CSS.<\/p>\n

<\/p>\n

Single file components<\/h3>\n

\u200b\u200b<\/p>\n

React does not have an opinion about how styles are defined
\n—React Documentation<\/small>\u200b<\/p><\/blockquote>\n

\u200b<\/p>\n

\u200b\u200b\u200b\u200b<\/p>\n

A UI framework that doesn’t have a built-in way to add styles to your components is unfinished.
\n—Rich Harris, creator of Svelte<\/small><\/p><\/blockquote>\n

\u200b<\/p>\n

In Svelte, you can write CSS in a stylesheet like you normally would on a typical project. You can also use CSS-in-JS solutions<\/a>, like styled-components<\/a> and Emotion<\/a>, if you’d like. It\u2019s become increasingly common to divide code into components, rather than by file type. React, for example, allows for the collocation of a components markup and JavaScript. In Svelte, this is taken one logical step further: the Javascript, markup and styling for a component can all exist together in a single `.svelte`\u200b file. If you\u2019ve ever used single file components in Vue, then Svelte will look familiar.<\/p>\n

\/\/ button.svelte\r\n<style>\r\n  button {\r\n    border-radius: 0;\r\n    background-color: aqua;\r\n  }\r\n<\/style>\r\n\r\n<button>\r\n  <slot\/>\r\n<\/button><\/code><\/pre>\n

Styles are scoped by default<\/h3>\n

By default, styles defined within a Svelte file are scoped<\/em>. Like CSS-in-JS libraries or CSS Modules, Svelte generates unique class names when it compiles to make sure the styles for one element never conflict with styles from another.<\/p>\n

That means you can use simple element selectors like div<\/code> and button<\/code> in a Svelte component file without needing to work with class names. If we go back to the button styles in our earlier example, we know that a ruleset for <button><\/code> will only be applied to our <Button><\/code> component \u2014 not to any other HTML button elements within the page. If you were to have multiple buttons within a component and wanted to style them differently, you’d still need classes. Classes will also be scoped by Svelte. <\/p>\n

The classes that Svelte generates look like gibberish because they are based on a hash of the component styles (e.g. svelte-433xyz<\/code>). This is far easier than a naming convention like BEM<\/a>. Admittedly though, the experience of looking at styles in DevTools is slightly worse as the class names lack meaning.<\/p>\n

\"\"
The markup of a Svelte component in DevTools.<\/figcaption><\/figure>\n

It\u2019s not an either\/or situation. You can use Svelte\u2019s scoped styling along with a regular stylesheet. I personally write component specific styles within .svelte<\/code> files, but make use of utility classes defined in a stylesheet. For global styles to be available across an entire app \u2014 CSS custom properties, reusable CSS animations, utility classes, any \u2018reset\u2019 styles, or a CSS framework like Bootstrap \u2014 I suggest putting them in a stylesheet linked in the head of your HTML document.<\/p>\n

It lets us create global styles<\/h3>\n

As we’ve just seen, you can use a regular stylesheet to define global styles. Should you need to define any global styles from within a Svelte component, you can do that too by using :global<\/code>. This is essentially a way to opt out of scoping when and where you need to.<\/p>\n

For example, a modal component may want to toggle a class to style the body element:<\/p>\n

<style>\r\n:global(.noscroll) {\r\n  overflow: hidden;\r\n}\r\n<\/style><\/code><\/pre>\n

Unused styles are flagged<\/h3>\n

Another benefit of Svelte is that it will alert you about any unused styles during compilation. In other words, it searches for places where styles are defined but never used in the markup. <\/p>\n

Conditional classes are terse and effortless<\/h3>\n

If the JavaScript variable name and the class name is the same, the syntax is incredibly terse. In this example, I\u2019m creating modifier props for a full-width button and a ghost button. <\/p>\n

<script>\r\n  export let big = false;\r\n  export let ghost = false;\r\n<\/script>\r\n\r\n<style>\r\n  .big {\r\n    font-size: 20px;\r\n    display: block;\r\n    width: 100%;\r\n  }\r\n  \r\n  .ghost {\r\n    background-color: transparent;\r\n    border: solid currentColor 2px;\r\n  }\r\n<\/style>    \r\n    \r\n<button class:big class:ghost>\r\n  <slot\/>\r\n<\/button><\/code><\/pre>\n

A class of ghost<\/code> will be applied to the element when a ghost<\/code> prop is used, and a class of big<\/code> is applied when a big<\/code> prop is used. <\/p>\n

<script>\r\n  import Button from '.\/Button.svelte';\r\n<\/script>\r\n\r\n<Button big ghost>Click Me<\/Button><\/code><\/pre>\n

Svelte doesn\u2019t require class names and prop names to be identical.<\/p>\n

<script>\r\n  export let primary = false;\r\n  export let secondary = false;\r\n<\/script>\r\n\r\n<button\r\n  class:c-btn--primary={primary}\r\n  class:c-btn--secondary={secondary}\r\n  class=\"c-btn\">\r\n  <slot><\/slot>\r\n<\/button><\/code><\/pre>\n

The above button component will always have a c-btn<\/code> class but will include modifier classes only when the relevant prop is passed in, like this:<\/p>\n

<Button primary>Click Me<\/Button><\/code><\/pre>\n

That will generate this markup:<\/p>\n

<button class=\"c-btn c-btn--primary\">Click Me<\/button><\/code><\/pre>\n

Any number of arbitrary classes can be passed to a component with a single prop:<\/p>\n

<script>\r\nlet class_name = '';\r\nexport { class_name as class };\r\n<\/script>\r\n\r\n<button class=\"c-btn {class_name}\">\r\n  <slot \/>\r\n<\/button><\/code><\/pre>\n

Then, classes can be used much the same way you would with HTML markup:<\/p>\n

<Button class=\"mt40\">Click Me<\/Button><\/code><\/pre>\n

From BEM to Svelte<\/h3>\n

Let’s see how much easier Svelte makes writing styles compared to a standard CSS naming convention. Here’s a simple component coded up using BEM. <\/p>\n

.c-card {\r\n  border-radius: 3px;\r\n  border: solid 2px;\r\n}\r\n\r\n.c-card__title {\r\n  text-transform: uppercase;\r\n}\r\n\r\n.c-card__text {\r\n  color: gray;\r\n}\r\n\r\n.c-card--featured {\r\n  border-color: gold;\r\n}<\/code><\/pre>\n

Using BEM, classes get long and ugly. In Svelte, things are a lot simpler. <\/p>\n

<style>\r\ndiv {\r\n  border-radius: 3px;\r\n  border: solid 2px;\r\n}\r\n\r\nh2 {\r\n  text-transform: uppercase;\r\n}\r\n\r\np {\r\n  color: gray;\r\n}\r\n\r\n.featured {\r\n  border-color: gold;\r\n}\r\n<\/style>\r\n\r\n<div class:featured>\r\n  <h2>{title}<\/h2>\r\n  <p>\r\n    <slot \/>\r\n  <\/p>\r\n<\/div><\/code><\/pre>\n

It plays well with preprocessors<\/h3>\n

CSS preprocessors feels a lot less necessary when working with Svelte, but they can work perfectly alongside one another by making use of a package called Svelte Preprocess. Support is available for Less, Stylus and PostCSS, but here we’ll look at Sass. The first thing we need to do is to install some dependencies:<\/p>\n

npm install -D svelte-preprocess node-sass<\/code><\/pre>\n

Then we need to import autoPreprocess in rollup.config.js<\/code> at the top of the file. <\/p>\n

import autoPreprocess from 'svelte-preprocess';<\/code><\/pre>\n

Next, let\u2019s find the plugins array and add preprocess: autoPreprocess()<\/code> to Svelte:<\/p>\n

export default {\r\n  plugins: [\r\n    svelte({\r\n      preprocess: autoPreprocess(),\r\n      ...other stuff<\/code><\/pre>\n

Then all we need to do is specify that we\u2019re using Sass when we\u2019re working in a component file, using type=\"text\/scss\"<\/code> or lang=\"scss\"<\/code> to the style tag. <\/p>\n

<style type=\"text\/scss\">\r\n  $pink: rgb(200, 0, 220);\r\n  p {\r\n    color: black;\r\n    span {\r\n      color: $pink;\r\n    }\r\n  }\r\n<\/style><\/code><\/pre>\n

Dynamic values without a runtime<\/h3>\n

We\u2019ve seen that Svelte comes with most of the benefits of CSS-in-JS out-of-the-box \u2014 but without external dependencies! However, there\u2019s one thing that third-party libraries can do that Svelte simply can\u2019t: use JavaScript variables in CSS. <\/p>\n

The following code is not valid and will not work:<\/strong><\/p>\n

<script>\r\n  export let cols = 4;\r\n<\/script>\r\n\r\n<style>\r\n  ul {\r\n    display: grid;\r\n    width: 100%;\r\n    grid-column-gap: 16px;\r\n    grid-row-gap: 16px;\r\n    grid-template-columns: repeat({cols}, 1fr);\r\n  }\r\n<\/style>\r\n\r\n<ul>\r\n  <slot \/>\r\n<\/ul><\/code><\/pre>\n

We can, however, achieve similar functionality by using CSS variables. <\/p>\n

<script>\r\n  export let cols = 4;\r\n<\/script>\r\n\r\n<style>\r\n  ul {\r\n    display: grid;\r\n    width: 100%;\r\n    grid-column-gap: 16px;\r\n    grid-row-gap: 16px;\r\n    grid-template-columns: repeat(var(--columns), 1fr);\r\n  }\r\n<\/style>\r\n\r\n<ul style=\"--columns:{cols}\">\r\n  <slot \/>\r\n<\/ul><\/code><\/pre>\n
\n

I\u2019ve written CSS in all kinds of different ways over the years: Sass, Shadow DOM, CSS-in-JS, BEM, atomic CSS and PostCSS. Svelte offers the most intuitive, approachable and user-friendly styling API. If you want to read more about this topic then check out the aptly titled The Zen of Just Writing CSS<\/a> by Rich Harris.<\/p>\n","protected":false},"excerpt":{"rendered":"

There\u2019s been a lot of well-deserved hype around Svelte recently, with the project accumulating over 24,000 GitHub stars. Arguably the simplest JavaScript framework out there, Svelte was written by Rich Harris, the developer behind Rollup. There\u2019s a lot to like about Svelte (performance, built-in state management, writing proper markup rather than JSX), but the big […]<\/p>\n","protected":false},"author":245300,"featured_media":297744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[730,1535,891,6782],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/10\/svelte-logo-outline.png?fit=1200%2C600&ssl=1","jetpack-related-posts":[{"id":302768,"url":"https:\/\/css-tricks.com\/getting-acquainted-with-svelte-the-new-framework-on-the-block\/","url_meta":{"origin":297067,"position":0},"title":"Getting Acquainted With Svelte, the New Framework on the Block","date":"February 6, 2020","format":false,"excerpt":"For the last six years, Vue, Angular, and React have run the world of front-end component frameworks. Google and Facebook have their own sponsored frameworks, but they might leave a bitter taste for anyone who advocates for an open and unbiased web. Vue is another popular framework that has multiple\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/svelte-radiant.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":335636,"url":"https:\/\/css-tricks.com\/how-to-use-tailwind-on-a-svelte-site\/","url_meta":{"origin":297067,"position":1},"title":"How to Use Tailwind on a Svelte Site","date":"March 12, 2021","format":false,"excerpt":"Let\u2019s spin up a basic Svelte site and integrate Tailwind into it for styling. One advantage of working with Tailwind is that there isn\u2019t any context switching going back and forth between HTML and CSS, since you\u2019re applying styles as classes right on the HTML. It\u2019s all the in same\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/tailwind-in-svelte.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":337926,"url":"https:\/\/css-tricks.com\/sveltekit-is-in-public-beta\/","url_meta":{"origin":297067,"position":2},"title":"SvelteKit is in public beta","date":"April 7, 2021","format":false,"excerpt":"Rich Harris: Think of it as\u00a0Next\u00a0for Svelte. It's a framework for building apps with Svelte, complete with server-side rendering, routing, code-splitting for JS and CSS, adapters for different serverless platforms and so on. Great move. I find Next.js a real pleasure to work with. I've hit some rough edges trying\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/04\/svelte-blobs.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":355937,"url":"https:\/\/css-tricks.com\/introducing-svelte-and-comparing-svelte-with-react-and-vue\/","url_meta":{"origin":297067,"position":3},"title":"Introducing Svelte, and Comparing Svelte with React and Vue","date":"November 4, 2021","format":false,"excerpt":"Josh Collingsworth is clearly a big fan of Svelte, so while this is a fun and useful comparison article, it's here to crown Svelte the winner all the way through. A few things I find compelling: One of the things I like most about Svelte is its HTML-first philosophy. With\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/11\/svelte-react-vue.png?fit=1200%2C312&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":360239,"url":"https:\/\/css-tricks.com\/make-a-component-multiple-frameworks-in-a-monorepo\/","url_meta":{"origin":297067,"position":4},"title":"How to Make a Component That Supports Multiple Frameworks in a Monorepo","date":"January 5, 2022","format":false,"excerpt":"Your mission \u2014 should you decide to accept it \u2014 is to build a Button component in four frameworks, but, only use one button.css file! This idea is very important to me. I\u2019ve been working on a component library called AgnosticUI where the purpose is building UI components that aren\u2019t\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/button-frameworks.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":340825,"url":"https:\/\/css-tricks.com\/svelte-for-the-experienced-react-dev\/","url_meta":{"origin":297067,"position":5},"title":"Svelte for the Experienced React Dev","date":"May 21, 2021","format":false,"excerpt":"This post is an accelerated introduction to Svelte from the point of view of someone with solid experience with React. I\u2019ll provide a quick introduction, and then shift focus to things like state management and DOM interoperability, among other things. I plan on moving somewhat quickly, so I can cover\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/05\/svelte-react-code.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/10\/svelte-logo-outline.png?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/297067"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/245300"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=297067"}],"version-history":[{"count":10,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/297067\/revisions"}],"predecessor-version":[{"id":297297,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/297067\/revisions\/297297"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/297744"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=297067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=297067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=297067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}