Themes

This guide is an entry point for working with themes

Concept

To shadow (override) - to create a file which will be used in the application instead of some other file. You can shadow files of any extension and any parent theme.

Set up a parent theme

First of all, you will need to create a parent theme. A parent theme is a project that will be used as a base for a child theme, which will modify some of its functionality.

Let's have a parent theme configured as follows.

./packages/@example/parent-theme/package.json
{
    "name": "@example/parent-theme",
    "mosaic": {
        "type": "theme",
        "themeAlias": "Parent"
    }
}

And let's put the following file there. This file we are going to override a bit later.

./packages/@example/parent-theme/src/data-source.js
export const SOME_VALUE = 42;

export class DataSource {
    getData() {
        return ['Source data'];
    }
    
    run() {
        const data = this.getData();
        console.log(data);
    }
};

export default DataSource;

Also, let's use the functionality from above in that parent theme!

./packages/@example/parent-theme/src/index.js
import DataSource from './data-source';

const dataSource = new DataSource();

dataSource.run()

The this parent theme will output the following logging. This will change when we introduce a child theme!

['Source data']

Set up a child theme

Then, let's create a child theme. A child theme will extend the parent theme's functionality and modify it.

We will configure the child theme as follows. We set the parent theme to the theme we have created above. It is important to install the parent theme into this project!

./package.json
{
    "mosaic": {
        "type": "theme",
        "parentTheme": "@example/parent-theme"
    },
    "dependencies": {
        "@example/parent-theme": "file:./packages/child-theme"
    },
    "scripts": {
        "postinstall": "...-scripts link",
        "postupdate": "...-scripts link"
    }
}

Shadow

In the child theme, we will create a file with the same path as above, and modify some functionality.

./src/data-source.js
import { 
    DataSource as BaseDataSource,
    SOME_VALUE
} from '@example/parent-theme';

export { SOME_VALUE };

export class DataSource extends BaseDataSource {
    getData() {
        return ['Data from the override'];
    }
};

export default DataSource;

We are not going to create any additional files in our child theme. We wanted to change a single thing and we did it!

Ensure all exports

Remember to re-export all of the initial file's exports, which you are not overriding. This way we ensure nothing will get lost after the modifications. This is done to the SOME_VALUE in the example above.

If you forget to re-export something, the application will lose any access to these values.

Review project structure

See the full project structure below.

.
├── 📁 packages/
   └── 📁 @example/
       └── 📁 parent-theme/
           ├── 📁 src/
              ├── 📄 data-source.js
              └── 📄 index.js
           └── 📄 package.json
├── 📁 src/
   └── 📄 data-source.js
└── 📄 package.json

Demonstration

Let's now see what happens when we run the project. We are going to see the logging from the run method, which was kept intact. But the value of this logging is absolutely different!

['Data from the override']

As you see, during the project build resolution of all of the files fell back to the parent theme, except one file we have overridden. Our override changed the part of functionality we were willing to change and now the theme works differently.

How cool is that! Find out more in the guide above.

File shadowing

Last updated