Monaca Onsen UI Discord Chat Github Repo

Change Color Theme by Page



  • Using Onsen-Vue. Is it possible to programmatically specify a different color theme for each page?



  • Summary: What I was trying to do was dynamically change the color of an Onsens-Vue page based on which page was being displayed.

    The best solution I found is to use CSS variables. I copied the file “onsenui/css/dark-onsen-css-components.css” into a “theme” folder in my project, then replaced the hard-coded colors with css variables, such as:

    .page {
      font-family: -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', 'Lucida Grande', sans-serif;
    ...
      background-color: var(--background-color);
    ...
    }
    

    (Note that I started with the dark theme because it was easier to spot and replace the unique colors with their own CSS variables. Whereas with the light theme, many different colors were all simply white.)

    In my copied CSS file, I also set the initial values:

    :root {
      /* variables for iOS components */
      --color: #fff;
      --background-color: #0d0d0d;
      --toolbar-background-color: #181818;
    
      /* variables for Material Design components */
      --material-background-color: #303030;
      --material-toolbar-background-color: #212121;
    
      /* others */
    }
    

    Of course, I had to adjust my import statement in main.ts to point to my “theme” folder instead of “onsenui”:

    import './theme/dark-onsen-css-components.css';
    

    Next I created a PageHandler.vue SFC to contain a Mixin that will be used in all of my pages. This mixin contains a single function handleMounted() that is passed a variable by the page indicating whether we want light or dark colors. Then I set the colors by using the setProperty() function on the style:

    <template>
      <div class="page-handler">
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component
    export default class PageHandler extends Vue {
      public handleMounted(darkTheme: boolean) {
        const el = document.documentElement;
        if (el) {
          const style = el.style;
          if (style) {
            if (darkTheme) {
              style.setProperty('--background-color', '#0d0d0d');
              style.setProperty('--color', '#fff');
              style.setProperty('--material-background-color', '#303030');
              style.setProperty('--toolbar-background-color', '#181818');
              style.setProperty('--material-toolbar-background-color', '#212121');
              // etc.
            } else {
              style.setProperty('--background-color', '#efeff4');
              style.setProperty('--color', '#1f1f21');
              style.setProperty('--material-background-color', '#eceff1');
              style.setProperty('--toolbar-background-color', '#fafafa');
              style.setProperty('--material-toolbar-background-color', '#fff');
              // etc.
            }
          }
        }
      }
    }
    </script>
    

    Then I called the mixin from within the mounted() method on each page:

    <template class="about-page">
      <v-ons-page>
        <MyToolbar title="About"/>
        <h1>This is the About page!</h1>
      </v-ons-page>
    </template>
    
    <script lang="ts">
    import { Component, Mixins, Vue } from 'vue-property-decorator';
    import PageHandler from '@/components/PageHandler.vue';
    import MyToolbar from '@/components/Toolbar.vue';
    
    @Component({
      components: {
        PageHandler,
        MyToolbar,
      },
    })
    export default class AboutPage extends Mixins(PageHandler) {
      private mounted() {
        this.handleMounted(true);  // show dark theme
      }
    }
    </script>
    

    And it works great!