Socket
Socket
Sign inDemoInstall

svelte-preprocess-cssmodules

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte-preprocess-cssmodules

Svelte preprocessor to generate CSS Modules classname on Svelte components


Version published
Weekly downloads
1.4K
increased by21.02%
Maintainers
1
Weekly downloads
 
Created
Source

Svelte preprocess CSS Modules

Generate CSS Modules classname on Svelte components

npm install --save-dev svelte-preprocess-cssmodules

Usage

Add module attribute to the <style> tag to enable cssModules in the component.

<style module>
  p { font-size: 14px; }
  .red { color: red; }
</style>

<p class="red">My red text</p>

The component will be transformed to

<style>
  p { font-size: 14px; }
  .red-30_1IC { color: red; }
</style>

<p class="red-30_1IC">My red text</p>

Modes

Preprocessor can operate in the following modes:

  • native (default) - scopes classes with cssModules, anything else is unscoped
  • mixed - scopes non-class selectors with svelte scoping in addition to native (same as preprocessor v1)
  • scoped - scopes classes with svelte scoping in addition to mixed

The mode can be set globally from the preprocessor options or locally to override the global settings per component.

Mixed mode

<style module="mixed">
  p { font-size: 14px; }
  .red { color: red; }
</style>

<p class="red">My red text</p>

generating

<style>
  p.svelte-teyu13r { font-size: 14px; }
  .red-30_1IC { color: red; }
</style>

<p class="red-30_1IC svelte-teyu13r">My red text</p>

Scoped mode

<style module="scoped">
  p { font-size: 14px; }
  .red { color: red; }
</style>

<p class="red">My red text</p>

generating

<style>
  p.svelte-teyu13r { font-size: 14px; }
  .red-30_1IC.svelte-teyu13r { color: red; }
</style>

<p class="red-30_1IC svelte-teyu13r">My red text</p>

Class directive

Toggle a class on an element.

<script>
  let isActive = true;
</script>

<style module>
  .bold { font-weight: bold; }
</style>

<p class:bold={isActive}>My red text</p>
<p class="{isActive ? 'bold' : ''}">My blue text</p>

Generating

<style>
  .bold-2jIMhI { font-weight: bold; }
</style>

<p class="bold-2jIMhI">My red text</p>
<p class="bold-2jIMhI">My blue text</p>

Use of shorthand

<script>
  let active = true;
</script>

<style module>
  .active { font-weight: bold; }
</style>

<p class:active>My active text</p>

Generating

<style>
  .active-2jIMhI { font-weight: bold; }
</style>

<p class="active-2jIMhI">My active text</p>

Local selector

Force a selector to be scoped within a component to prevent style inheritance on child components.

:local() is doing the opposite of :global() and can only be used with the native and mixed mode on.

<!-- Parent Component-->

<style module>
  .main em { color: grey; }
  .main :local(strong) { font-weight: 900; }
</style>

<div class="main">
  <p>My <em>main</em> lorem <strong>ipsum tuye</strong></p>
  <ChildComponent />
</div>
<!-- Child Component-->

<style module>
  /** Rule to override parent style **/
  .child em { color: black; }

  /** 
   * Not needed rule because of the use of :local()
   .child strong { font-weight: 700 }
   */
</style>

<p class="child">My <em>secondary</em> lorem <strong>ipsum tuye</strong></p>

Generating

<!-- Parent Component-->

<style>
  .main-Yu78Wr em { color: grey; }
  .main-Yu78Wr strong.svelte-ery8ts { font-weight: 900; }
</style>

<div class="main-Yu78Wr">
  <p>My <em>main</em> lorem <strong class="svelte-ery8ts">ipsum tuye</strong></p>
  <ChildComponent />
</div>
<!-- Child Component-->

<style>
  .child-uhRt2j em { color: black; }
</style>

<p class="child-uhRt2j">My <em>secondary</em> lorem <strong>ipsum tuye</strong></p>

Import styles from an external stylesheet

Alternatively, styles can be created into an external file and imported onto a svelte component from the <script> tag. The name referring to the import can then be used in the markup targetting any existing classname of the stylesheet.

The css file must follow the convention FILENAME.module.css in order to be processed.

Note: The import option is only meant for stylesheets relative to the component. You will have to set your own bundler in order to import node_modules packages css files.

/** style.module.css **/
.red { color: red; }
.blue { color: blue; }
<!-- Svelte component -->
<script>
  import style from './style.module.css';
</script>

<p class={style.red}>My red text</p>
<p class={style.blue}>My blue text</p>

Generated code

<style>
  .red-en-6pb { color: red; }
  .blue-oVk-n1 { color: blue; }
</style>

<p class="red-en-6pb">My red text</p>
<p class="blue-oVk-n1">My blue text</p>

Destructuring import

/** style.module.css **/
section { padding: 10px; }
.red { color: red; }
.blue { color: blue; }
.bold { font-weight: bold; }
<!-- Svelte component -->
<script>
  import { red, blue } from './style.module.css';
</script>

<section>
  <p class={red}>My <span class="bold">red</span> text</p>
  <p class="{blue} bold">My blue text</p>
</section>

Generated code

<style>
  section { padding: 10px; }
  .red-1sPexk { color: red; }
  .blue-oVkn13 { color: blue; }
  .bold-18te3n { font-weight: bold; }
</style>

<section>
  <p class="red-1sPexk">My <span class="bold-18te3n">red</span> text</p>
  <p class="blue-oVkn13 bold-18te3n">My blue text</p>
</section>

kebab-case situation

The kebab-case classnames are being transformed to a camelCase version on imports to facilitate their use on Markup and Javascript.

/** style.module.css **/
.success { color: green; }
.error-message {
  color: red;
  text-decoration: line-through;
}
<script>
  import css from './style.module.css';
</script>

<p class={css.success}>My success text</p>
<p class="{css.errorMessage}">My error message</p>

<!-- OR -->

<script>
  import { success, errorMessage } from './style.module.css';
</script>

<p class={success}>My success message</p>
<p class={errorMessage}>My error message</p>

Generated code

<style>
  .success-3BIYsG { color: green; }
  .error-message-16LSOn {
    color: red;
    text-decoration: line-through;
  }
</style>

<p class="success-3BIYsG">My success messge</p>
<p class="error-message-16LSOn">My error message</p>

Unnamed import

If a css file is being imported without a name, the cssModules will still be applied to the classes of the stylesheet.

/** style.module.css **/
p { font-size: 18px; }
.success { color: green; }
<script>
  import './style.module.css'
</script>

<p class="success">My success message</p>
<p>My another message</p>

Generated code

<style>
  p { font-size: 18px; }
  .success-vg78j0 { color: green; }
</style>

<p class="success-vg78j0">My success messge</p>
<p>My error message</p>

Directive and Dynamic class

Use the Svelte's builtin class: directive or javascript template to display a class dynamically.
Note: the shorthand directive is NOT working with imported CSS Module identifiers.

<script>
  import { success, error } from './style.module.css';

  let isSuccess = true;
  $: notice = isSuccess ? success : error;
</script>

<button on:click={() => isSuccess = !isSuccess}>Toggle</button>

<!-- Error -->
<p class:success>Success</p>

<!-- Ok -->
<p 
  class:success={isSuccess}
  class:error={!isSuccess}>Notice</p>

<p class={notice}>Notice</p>
<p class={isSuccess ? success : error}>Notice</p>

Configuration

Rollup

To be used with the plugin rollup-plugin-svelte.

import svelte from 'rollup-plugin-svelte';
import cssModules from 'svelte-preprocess-cssmodules';

export default {
  ...
  plugins: [
    svelte({
      preprocess: [
        cssModules(),
      ]
    }),
  ]
  ...
}

Webpack

To be used with the loader svelte-loader.

const cssModules = require('svelte-preprocess-cssmodules');

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.svelte$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'svelte-loader',
            options: {
              preprocess: [
                cssModules(),
              ]
            }
          }
        ]
      }
    ]
  }
  ...
}

SvelteKit

As the module distribution is targetting esnext, Node.js 14 or above is required in order to work.

// svelte.config.js

import cssModules from 'svelte-preprocess-cssmodules';

const config = {
  ...
  preprocess: [
    cssModules(),
  ]
};

export default config;

Svelte Preprocess

Chaining several preprocessors may lead to errors if the svelte parser and walker is being manipulated multiple time. This issue is due to the way svelte runs its preprocessor in two phases. Read more here

In that situation, we recommend the use of the package svelte-as-markup-preprocessor.

npm install --save-dev svelte-as-markup-preprocessor

Example with typescript

// import packages
const { typescript } = require('svelte-preprocess');
const { asMarkupPreprocessor } = require('svelte-as-markup-preprocessor');
const cssModules = require('svelte-preprocess-cssmodules');

...

// svelte config
  preprocess: [
    asMarkupPreprocessor([
      typescript()
    ]),
    cssModules()
  ],
...

Options

Pass an object of the following properties

NameTypeDefaultDescription
modenative|mixed|scopednativeThe preprocess mode to use
localIdentName{String}"[local]-[hash:base64:6]"A rule using any available token
hashSeeder{Array}['style', 'filepath', 'classname']An array of keys to base the hash on
allowedAttributes{Array}[]An array of attributes to parse along with class
includePaths{Array}[] (Any)An array of paths to be processed
getLocalIdentFunctionundefinedGenerate the classname by specifying a function instead of using the built-in interpolation

localIdentName

Inspired by webpack interpolateName, here is the list of tokens:

  • [local] the targeted classname
  • [ext] the extension of the resource
  • [name] the basename of the resource
  • [path] the path of the resource
  • [folder] the folder the resource is in
  • [contenthash] or [hash] (they are the same) the hash of the resource content (by default it's the hex digest of the md4 hash)
  • [<hashType>:contenthash:<digestType>:<length>] optionally one can configure
    • other hashTypes, i. e. sha1, md4, md5, sha256, sha512
    • other digestTypes, i. e. hex, base26, base32, base36, base49, base52, base58, base62, base64
    • and length the length in chars

hashSeeder

Set the resource content of [hash] and [contenthash].

The list of available keys are:

  • style the content of the style tag (or the imported stylesheet)
  • filepath the path of the component
  • classname the local className

Example of use

// Preprocess config
...
preprocess: [
  cssModules({
    hashSeeder: ['filepath'],
  })
],
...
<button class="success">Ok</button>
<button class="cancel">Cancel</button>
<style module>
  .success { background-color: green; }
  .cancel { background-color: gray; }
</style>

Generating

<button class="success-yr6RT">Ok</button>
<button class="cancel-yr6RT">Cancel</button>
<style>
  .success-yr6RT { background-color: green; }
  .cancel-yr6RT { background-color: gray; }
</style>

allowedAttributes

Add other attributes than class to be parsed by the preprocesser

// Preprocess config
...
preprocess: [
  cssModules({
    allowedAttributes: ['data-color', 'classname'],
  })
],
...
<button class="red" data-color="red">Red</button>
<button class="red" classname="blue">Red or Blue</button>
<style module>
  .red { background-color: red; }
  .blue { background-color: blue; }
</style>

Generating

<button class="red-yr6RT" data-color="red-yr6RT">Red</button>
<button class="red-yr6RT" classname="blue-aE4qW">Red or Blue</button>
<style>
  .red-yr6RT { background-color: red; }
  .blue-aE4qW { background-color: blue; }
</style>

getLocalIdent

Customize the creation of the classname instead of relying on the built-in function.

function getLocalIdent(
  context: {
    context: string, // the context path
    resourcePath: string, // path + filename
  },
  localIdentName: {
    template: string, // the template rule
    interpolatedName: string, // the built-in generated classname
  },
  className: string, // the classname string
  content: { 
    markup: string, // the markup content
    style: string,  // the style content
  }
): string {
  return `your_generated_classname`;
}

Example of use

# Directory
SvelteApp
└─ src
   ├─ App.svelte
   └─ components
      └─ Button.svelte
<!-- Button.svelte -->
<button class="red">Ok</button>

<style>
  .red { background-color: red; }
</style>
// Preprocess config
...
preprocess: [
  cssModules({
    localIdentName: '[path][name]__[local]',
    getLocalIdent: (context, { interpolatedName }) => {
      return interpolatedName.toLowerCase().replace('src_', '');
      // svelteapp_components_button__red;
    }
  })
],
...

Migrating from v1

If you want to migrate an existing project to v2 keeping the approach of the 1st version, follow the steps below:

  • Set the mixed mode from the global settings.
    // Preprocess config
    ...
    preprocess: [
     cssModules([
       mode: 'mixed',
     ]),
    ],
    ...
    
  • Remove all $style. prefix from the html markup
  • Add the attribute module to <style> within your components.
    <style module>
    ...
    </style>
    

Code Example

Rollup Config

export default {
  ...
  plugins: [
    svelte({
      preprocess: [
        cssModules({
          includePaths: ['src'],
          localIdentName: '[hash:base64:10]',
        }),
      ]
    }),
  ]
  ...
}

Svelte Component using <style>

<style module>
  .modal {
    position: fixed;
    ...
  }
  header {
    ...
  }
  .body {
    flex: 1 0 0;
    ...
  }
  footer {
    ...
  }
  button {
    ...
  }
  .cancel {
    background-color: #f2f2f2;
  }
</style>

<section class="modal">
  <header>My Modal Title</header>
  <div class="body">
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p>
  </div>
  <footer>
    <button>Ok</button>
    <button class="cancel">Cancel</button>
  </footer>
</section>

OR Svelte Component using import

/** style.module.css */
.modal {
  position: fixed;
  ...
}

[...]
<script>
  import style from './style.module.css';
</script>

<section class={style.modal}>
  <header>My Modal Title</header>
  <div class={style.body}>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p>
  </div>
  <footer>
    <button>Ok</button>
    <button class={style.cancel}>Cancel</button>
  </footer>
</section>

Final html code generated by svelte

<style>
  ._329TyLUs9c {
    position: fixed;
    ...
  }
  header {
    ...
  }
  ._1HPUBXtzNG {
    flex: 1 0 0;
    ...
  }
  footer {
    ...
  }
  button {
    ...
  }
  ._1xhJxRwWs7 {
    background-color: #f2f2f2;
  }
</style>

<section class="_329TyLUs9c">
  <header>My Modal Title</header>
  <div class="_1HPUBXtzNG">
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p>
  </div>
  <footer>
    <button>Ok</button>
    <button class="_1xhJxRwWs7">Cancel</button>
  </footer>
</section>

Why CSS Modules on Svelte

While the native CSS Scoped system should be largely enough to avoid class conflict, it could find its limit when working on a hybrid project. On a non full Svelte application, the thought on the class naming would not be less different than what we would do on a regular html page. For example, on the modal component above, It would have been wiser to namespace some of the classes such as .modal-body and .modal-cancel in order to prevent inheriting styles from other .body and .cancel classes.

License

MIT

Keywords

FAQs

Package last updated on 06 Oct 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc