csssveltesveltekitpostcsssvelte-5

Normalizing CSS Globally


I am building a SSG website that is using SvelteKit (v2.0.0) and Svelte (v5.0.0). I am also using PostCSS using the vitePreprocess integration.

My project is the default SvelteKit project it generates upon running sv create with the addition of the SSG adapter, PostCSS and a +layout.svelte file in src/routes to apply global CSS properties to the +page.svelte file in the same directory.

This is what +layout.svelte looks like:

<script lang="ts">
    let { children } = $props();
</script>

<style global lang='postcss'>
    @import 'sanitize.css';

    :root {
        --green: #99dd11;
        --blue: #113399;
    }
</style>

{@render children()}

In +layout.svelte's current form, I am using the sanitize.css npm package. I've also attempted to use the postcss-normalize npm package, replacing @import 'sanitize.css'; with @import-sanitize; with the same result.

In this current form I am able to use the CSS variables in +page.svelte I've set, green and blue, but the style sheet Svelte generates (both with pnpm run dev and pnpm run build && pnpm run preview) does not include any of the styles defined in sanitize.css. Even when I don't use either variable in +page.svelte they are still included in the generated CSS file as they are defined in the global scope.

With the included global in +layout.svelte's style tag I was under the impression that sanitize.css would be applied to every +page.svelte file in the same directory (and sub directories) as +layout.svelte.

Furthermore, after reading THIS StackOverflow post I felt that my current code should have worked. I am aware this was made 3 years ago and uses a version of Svelte before v5.0.0.

Here are some other files that might be worth including in finding the solution to this issue:

svelte.config.js

import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: vitePreprocess([vitePreprocess()]),
    kit: {
        adapter: adapter({
            fallback: '404.html'
        }),
        paths: {
            base: process.argv.includes('dev') ? '' : process.env.BASE_PATH
        }
    }
};

export default config;

postcss.config.js

import postcssPresetEnv from 'postcss-preset-env';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';

const config = {
    plugins: [postcssPresetEnv(), autoprefixer(), cssnano()]
};

export default config;

+page.svelte

<style lang="postcss">
    p {
        user-select: none;
        width: 500px;
        @media (max-width: 500px) {
            width: auto;
        }
    }

    h1 {
        color: var(--blue);
        a {
            transition: all 0.5s;
            &:hover {
                color: var(--green);
            }
        }
    }
</style>

<h1>Welcome to <a href="https://example.com/">example.com</a></h1>
<p>
    This website was built using SvelteKit's static site generator and hosted on Cloudflare Pages.
</p>

Edit: I made a few other attempts to resovle this, none of which worked.


Solution

  • Removing @import 'sanitize.css'; from the <style> tag and adding import 'sanitize.css'; to the <script> tag resolves the issue.

    I came to this solution when reading the "Where should I put my global styles?" section of the FAQ for vite-plugin-svelte

    This pointed out three issues with my original +layout.svelte file:

    1. Global styles should always be placed in their own stylesheet files whenever possible, and not in a Svelte component's tag

    2. The stylesheet files can then be imported directly in JS and take advantage of Vite's own style processing.

    3. The global attribute in my <style> tag is a feature from svelte-preprocess, which I was not using or had plans to use. If I were to use global styles in the <style> tag I should instead use the nested :global { ... } syntax, which not is recommended for global styles.

    The fixed +layout.svelte looks like this:

    <script lang="ts">
        import 'sanitize.css';
        let { children } = $props();
    </script>
    
    {@render children()}