CSS

Rspeedy supports different ways of styling your application, including:

  • CSS Modules: Create locally scoped CSS classes to avoid naming conflicts and improve maintainability.

  • Global CSS: Simple to use and familiar for those experienced with traditional CSS, but can lead to larger CSS bundles and difficulty managing styles as the application grows.

  • CSS pre-processors: Popular CSS pre-processors like sass and less that extend CSS with features like variables, nested rules, and mixins.

  • (Legacy) Lynx Scoped CSS: Write like global CSS, but work like CSS Modules.

  • PostCSS: A tool for transforming CSS.

Using CSS Modules

CSS Modules allows us to write CSS code in a modular way, and these styles can be imported and used in JavaScript files. Using CSS Modules can automatically generate unique class names, isolate styles between different modules, and avoid class name conflicts.

TIP

You can use Global CSS if you want some of the CSS to be non-isolated.

Rspeedy supports CSS Modules by default, you don't need to add additional configuration. Our convention is to use the [name].module.css filename to enable CSS Modules.

Example

  1. Write styles as usual:
button.module.css
.red {
  background: red;
}
  1. Use styles like a module:
Button.jsx
import styles from './button.module.css'

export function Button() {
  return (
    <view className={styles.red}>
      <text>Button</text>
    </view>
  )
}

Or, you can use Named Imports:

Button.jsx
import { red } from './button.module.css'

export function Button() {
  return (
    <view className={red}>
      <text>Button</text>
    </view>
  )
}

With CSS Pre-Processor

The CSS Modules can also be used with CSS Pre-Processor. Just name your files with the pattern *.module.*.

E.g.: the following style files are considered CSS Modules:

  • *.module.css
  • *.module.less
  • *.module.sass
  • *.module.scss
  • *.module.styl
  • *.module.stylus

Recognition Rules

By default, only files ending with *.module.{css,scss,less} are recognized as CSS Modules.

If you want to treat other CSS files as CSS Modules as well, you can achieve this by configuring output.cssModules.auto.

For example:

lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  output: {
    cssModules: {
      auto(filename) {
        return filename.includes('.module.') || filename.includes('/shared/')
      },
    },
  },
})

Given this configuration, the following imports will be recognized as CSS Modules:

import * as foo from './foo.module.css'
import * as bar from './shared/bar.css'

Type Declaration

When you import CSS Modules in TypeScript code, TypeScript may prompt that the module is missing a type definition:

TS2307: Cannot find module './index.module.css' or its corresponding type declarations.

To fix this, you need to add a type declaration file for the CSS Modules, please create a src/rspeedy-env.d.ts file, and add the corresponding type declaration.

src/rspeedy-env.d.ts
/// <reference types="@lynx-js/rspeedy/client" />
TIP

create-rspeedy-app will automatically create this file for you.

If type errors still exist after adding the type declaration, you can try to restart the current IDE. Making sure the TypeScript can correctly identify the type definition.

Generate exact type declaration

The @lynx-js/rspeedy/client will give type declarations like this:

declare module '*.module.css' {
  type CSSModuleClasses = {
    readonly [key: string]: string
  }
  const classes: CSSModuleClasses
  export default classes
}

Using Typed CSS Modules Plugin with Rspeedy will generate type declaration files for all CSS Modules with exact type declarations.

  1. Install the @rsbuild/plugin-typed-css-modules package
npm
yarn
pnpm
bun
npm add -D @rsbuild/plugin-typed-css-modules
  1. Add the pluginTypedCSSModules to lynx.config.ts
lynx.config.ts
import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules'

import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'
import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginReactLynx(),
    pluginTypedCSSModules(),
  ],
})

After running rspeedy build or rspeedy dev, the type declarations will be generated.

button.module.css.d.ts
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  red: string
}
export const cssExports: CssExports
export default cssExports

You may also need to add "allowArbitraryExtensions": true and "moduleResolution": "Bundler" to tsconfig.json.

tsconfig.json
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "allowArbitraryExtensions": true
  }
}

Using Global CSS

In some case, you may want the CSS styles to be used with some complex selector. It is called Global CSS in ReactLynx.

Just write CSS code and imported from a javascript file.

Example

  1. Write styles as usual:
styles.css
.red {
  background: red;
}

.red > text {
  color: blue;
}
  1. Import the .css file from the JSX file and use the CSS classes:
index.jsx
import './styles.css'

export default function App() {
  return (
    <view className="red">
      <text>Hello, ReactLynx!</text>
    </view>
  )
}

Using CSS Pre-Processors

CSS pre-processors extend CSS with features like variables, nested rules, and mixins.

Usingsass

Simply create .scss or .sass files and import them into JavaScript.

import './global.sass'
import styles from './button.module.scss'

export function App() {
  return (
    <view className={styles.red}>
      <text className="title">Hello, Sass</text>
    </view>
  )
}
  1. Install the @rsbuild/plugin-sass package
npm
yarn
pnpm
bun
npm install -D @rsbuild/plugin-sass
  1. Add the pluginSass to lynx.config.ts
lynx.config.ts
import { pluginSass } from '@rsbuild/plugin-sass'

import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginSass({
      /** sass options */
    }),
  ],
})

More options can be used in pluginSass, please refer to Sass Plugin for usage.

Usingless

Simply create .less files and import them into JavaScript.

import './global.less'
import styles from './button.module.less'

export function App() {
  return (
    <view className={styles.red}>
      <text className="title">Hello, Less</text>
    </view>
  )
}
  1. Install the @rsbuild/plugin-less package
npm
yarn
pnpm
bun
npm install -D @rsbuild/plugin-less
  1. Add the pluginLess to lynx.config.ts
lynx.config.ts
import { pluginLess } from '@rsbuild/plugin-less'

import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginLess({
      /** less options */
    }),
  ],
})

More options can be used in pluginLess, please refer to Less Plugin for usage.

Usingstylus

  1. Install the @rsbuild/plugin-stylus package
npm
yarn
pnpm
bun
npm install -D @rsbuild/plugin-stylus
  1. Add the pluginStylus to lynx.config.ts
lynx.config.ts
import { pluginStylus } from '@rsbuild/plugin-stylus'

import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginStylus({
      /** stylus options */
    }),
  ],
})

More options can be used in pluginStylus, please refer to Stylus Plugin for usage.

Using Lynx Scoped CSS

You may NOT need scoped CSS

In most instances, there's no need to utilize scoped CSS. It's typically employed for compatibility with ReactLynx2.

Use CSS Modules instead.

Lynx Scoped CSS works just like CSS Modules. The CSS styles can only be used within the JavaScript files that import it.

Given the following CSS file:

styles.css
.red {
  color: red;
}

.bg {
  width: 100vw;
  height: 20px;
  background: blue;
}

The CSS styles can be imported and used:

import './styles.css'
import { Button } from './Button.jsx'

const jsx = <view className="bg"></view>

export function App() {
  return (
    <view>
      <text className="red">Hello, Scoped CSS</text>
      {jsx}
			<Button />
    </view>
  )
}

However, it will not be effective unless imported (this is where the concept of scope is applicable):

Button.jsx
export function Button() {
  return (
    // The use of `red` here has no effect.
    <view>
      <text className="red">Button</text>
    </view>
  )
}
TIP

Technically, the Scoped CSS can be used with CSS Modules. But it does not make sense since CSS Modules are already scoped.

If you would like to use CSS Modules, just safely disable Scoped CSS.

Set enableRemoveCSSScope to false to enable Scoped CSS.

lynx.config.ts
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'
import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginReactLynx({
      enableRemoveCSSScope: false,
    }),
  ],
})

Using PostCSS

Powered by Rsbuild, Rspeedy has built-in PostCSS to transform the CSS code.

Rsbuild uses postcss-load-config to load the PostCSS configuration file in the root directory of the current project, such as postcss.config.js:

export default {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375,
    },
  },
}

CSS minification

Rspeedy uses cssnano to minify the CSS code.

Disable CSS minification

You can disable CSS minification by setting output.minify.css to false.

lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy'

export default defineConfig({
  output: {
    minify: {
      css: false,
    }
  },
})

Customize CSS minification

You can use @rsbuild/plugin-css-minimizer to customize the CSS minimizer.

lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy'
import { pluginCssMinimizer } from '@rsbuild/plugin-css-minimizer'

export default defineConfig({
  plugins: [
    pluginCssMinimizer({
      /** Custom options */
    }),
  ],
})