Search code examples
typescripttailwind-csssvelteflowbite-svelte

Svelte: How to use template expressions in a 'style' block?


I am trying to create a simple Svelte Component (NumberFormatting)


<script lang="ts">
    export let number: string ;

    const formatNumber = (value: string) => {
        return Number(value).toFixed(2);
        
    };

    const isNegative = (value: string): boolean => {
        return Number(value) < 0;
    };
</script>

<main>
    <p>{formatNumber(number)}</p>
</main>

<style>
    main {
        text-align: right;
    }

    p {
        margin: 0; /* Remove default margin to avoid extra space */
        color: {isNegative(number) ? 'red' : 'inherit'};
    }
</style>

And I got this error


11:11:08 PM [vite] Pre-transform error: Error while preprocessing /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting.svelte - [postcss] postcss-nested: /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting.svelte.vite-preprocess.css:8:3: Missed semicolon
11:11:10 PM [vite] Error when evaluating SSR module /src/routes/test/+page.svelte: failed to import "/src/NumberFormatting.svelte"
|- CssSyntaxError: /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting.svelte.vite-preprocess.css:8:3: Missed semicolon
...
{
  name: 'CssSyntaxError',
  id: '/home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting.svelte',
  message: 'Error while preprocessing /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting2.svelte - [postcss] postcss-nested: /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting2.svelte.vite-preprocess.css:8:3: Missed semicolon',
  frame: '',
  code: '\n' +
    '\tmain {\n' +
    '\t\ttext-align: right;\n' +
    '\t}\n' +
    '\n' +
    '\tp {\n' +
    '\t\tmargin: 0; /* Remove default margin to avoid extra space */\n' +
    "\t\tcolor: {isNegative(2) ? 'red' : 'inherit'};\n" +
    '\t}\n',
  stack: 'CssSyntaxError: /home/xxxxx/TestProjects/svelteDemo/src/NumberFormatting.svelte.vite-preprocess.css:8:3: Missed semicolon\n' +
...
plugin: 'vite-plugin-svelte'

My postcss.config.cjs looks like this

const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');
const tailwindcssNesting = require('tailwindcss/nesting'); 
const postcssImport = require('postcss-import'); 

const config = {
    plugins: [
        //Some plugins, like tailwindcss/nesting, need to run before Tailwind,
        postcssImport(),
        tailwindcssNesting(),
        tailwindcss(),
        //But others, like autoprefixer, need to run after,
        autoprefixer
    ]
};

module.exports = config;

And my tailwindcss.config.cjs


/** @type {import('tailwindcss').Config}*/
const config = {
    content: [
        './src/**/*.{html,js,svelte,ts}',
        './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'
    ],

    theme: {
        extend: {
            colors: {
                // flowbite-svelte
                primary: {
                    50: '#FFF5F2',
                    100: '#FFF1EE',
                    200: '#FFE4DE',
                    300: '#FFD5CC',
                    400: '#FFBCAD',
                    500: '#FE795D',
                    600: '#EF562F',
                    700: '#EB4F27',
                    800: '#CC4522',
                    900: '#A5371B'
                }
            }
        }
    },

    plugins: [require('flowbite/plugin')],

    darkMode: 'class'
};

module.exports = config;

It seems that the function isNegative is not preprocessed. It works well when isNegative is not present in the style block

The versions used : vite: 5.0.3 svelte: 4.2.7 flowbite: 2.2.1 tailwindcss: 3.3.6 flowbite-svelte: 0.44.23 postcss: 8.4.32 postcss-load-config: 5.0.2


Solution

  • You have invalid CSS, hence the error:

    p {
      …
      color: {isNegative(number) ? 'red' : 'inherit'};
    }
    

    It seems you are trying to use some kind of JavaScript expression in the CSS but the CSS in the <style> tag is not parsed with this in mind.

    Instead, consider having two different rules in the <style> tag that apply for the different possible states, like:

    <main>
      <p class:red={isNegative(number)}>{formatNumber(number)}</p>
    </main>
    
    <style>
      main {
        text-align: right;
      }
    
      p {
        margin: 0; /* Remove default margin to avoid extra space */
        color: inherit;
      }
      
      .red {
        color: red;
      }
    </style>