Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

How can I create multi theme sapper app? #562

Closed
NikolayMakhonin opened this issue Feb 14, 2019 · 8 comments
Closed

How can I create multi theme sapper app? #562

NikolayMakhonin opened this issue Feb 14, 2019 · 8 comments
Labels

Comments

@NikolayMakhonin
Copy link

NikolayMakhonin commented Feb 14, 2019

Is the sapper designed to create applications with multiple themes? If so, how can I do this?

I see that the styles of each component are loaded at the end of the head tag with the generated class names. To add a theme I need, somehow, to put a file with a theme after the head tag and add readable classes inside the components. Of course - this is a bad decision, but I still do not see others.

Ideally, I would like the theme of the application to be divided between the components and will load in parts, but so that it is in one place in the project. And so that the user can switch themes without restarting the application. Is it possible at all?

Thanks.

@NikolayMakhonin
Copy link
Author

I think I found good solution, but it will require changes in sapper or svelte.

I can add theme style to the component like that:

<style>
    ... component styles ...

    body.theme-dark h1 {
        color: #00f;
    }
</style>

And we can change body class for quick switch theme.

To put all the styles of all the themes into a separate file, we can use the npm modules: svelte-preprocess + postcss-import.

And in each component there will be only one additional line:

<style>
    ... component styles ...

    @import '../styles/themes.css';
</style>

The Sapper will remove all the styles of other components and leave only styles that is necessary in the current component.

But the Sapper do not allow to add such theme styles:

Unused CSS selector
45:   }
46:
47:   .theme-dark h1 {
      ^
48:     color: #00f;
49:   }

What is strange:
Sapper removed this selector: .theme-dark h1
But kept this: .theme-dark > h1

@thgh
Copy link
Contributor

thgh commented Feb 14, 2019

If output size is not a concern, you can wrap all your theme related styles in :global()

:global(.theme-dark) h1 { color: blue }
/* is equivalent to */
:global(.theme-dark h1) { color: blue }

@NikolayMakhonin
Copy link
Author

Output size is a concern.

Is it possible to remove selectors that are not used and not wrapped in :global()?

:global(.theme-dark) h1 { // keep
    color: #00f;
}

:global(.theme-dark .not-used) { // keep
    color: #00f;
}

:global(.theme-dark) .not-used { // remove
    color: #00f;
}

@NikolayMakhonin
Copy link
Author

I came up with a solution close to good. The downside is that each component must have an identifier.

theme_dark.scss

:global(.theme-dark) {
    @if ($component == 'component 1') {
        h1 {
            color: #00f;
        }
    } @else if ($component == 'component 2') {
        h1 {
            color: #f00;
        }
    }
}

themes.scss

@import './layers/themes/theme_dark.scss';
@import './layers/themes/theme_light.scss';

Svelte component:

<style lang="scss">
    ... component default style ...

    /* Define component name */
    $component: 'component 1';

    /* Import all themes */
    @import '../styles/themes.scss';

</style>

@thgh
Copy link
Contributor

thgh commented Feb 14, 2019

Try this:

_layout.html

<svelte:head>
  <link rel="stylesheet" type="text/css" href="{theme}-theme.css">
</svelte:head>
<script>
export default {
  preload () {
    // Read user session or cookie or url param or ...
    return { theme: session.theme || 'dark' }
  }
}
</script>

dark-theme.css

:root {
  --primary-text: white;
}
body {
  background: black;
}

Svelte component:

<h1>Hello world</h1>
<style>
h1 {
  color: var(--primary-text);
}
</style>

@NikolayMakhonin
Copy link
Author

I think it is not good idea - to use CSS variables. It is suitable only in cases where the themes differ mainly in the colors and text sizes.
I rely on the fact that I have no idea what complexity there will be themes in the future. Therefore, I want to have maximum flexibility. CSS cascading gives me that flexibility. Because I don’t need to think up the names of the variables and add it to the component code for each added shadow or corner-radius, etc. I assume that there may be such a theme that will require the addition of 100 variables - this is unacceptable.

I am trying to improve my idea above. To identify components, we can use the path to the component file and use the svelte CSS preprocessor. Maybe all this can be arranged as a plugin later:

rollup.config.js

const themesPath = './src/styles/themes.scss'
const svelteOptions = {
    // see: https://github.com/Rich-Harris/svelte-preprocessor-demo
    preprocess: {
        style: async function ({content, filename, ...others}) {
            const componentId = await unresolve(filename) // used npm module "unresolve"
            // component Id will be: "svelte-component/src/component.svelte" or path relative "themesPath"

            const cssFooter = `$component: '${componentId}'; @import '${themesPath}';`

            ... insert cssFooter to the component style content ...

            // scss proprocessor
            sveltePreprocess.style.call(this, {content, filename, ...others})
        }
    }
}

theme_dark.scss

:global(.theme-dark) {
    // npm component should not be compiled
    $exportedFromNpm = 'svelte-component/src/component.svelte'

    @if ($component == $exportedFromNpm) {
        h1 {
            color: #00f;
        }
    } @else if ($component == '../routes/index.html') {
        h1 {
            color: #f00;
        }
    }
}

@NikolayMakhonin
Copy link
Author

I made a plugin for working with themes in svelte:
https://github.com/NikolayMakhonin/svelte-themes-preprocess

@benmccann
Copy link
Member

There is ongoing discussion about how to best support this in Svelte: sveltejs/svelte#1550 and sveltejs/rfcs#13

I'm going to close this since there's not much for Sapper to do. Please feel free to contribute to the issues in Svelte

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants