# Guide to Frontend Customisation
There are many ways to customise MFEs, and depending on what aspect you want to customise the mechanism for customisation will be different.
MFEs support a few different mechanisms for customisation:
- **Design Tokens** allow you to customise everything from the broad overall colors, and fonts to the curvature of buttons and down to the specific customisations like setting the background color for primary buttons when in hover state.
- **Slots** allow you to add, replace or modify content in any MFE page. With slots you can provide alternative implementations for existing components or inject additional content into an MFE.
Listed below are a couple of common scenarios for customisation:
I want to:
> [[replace or modify the header or footer of the MFE->header-footer]]
> [[change the fonts, colors, spacing or other styling of the MFE->simple-theme]]
> [[add, remove or modify elements in an MFE->plugin-slots]]# Header and Footer Customisation
The original approach to customising the header and footer was to replace them entirely with a custom component. However, this is no longer required.
The footer and header components now have a collection of slots. As such, you can now make modifications by adding, removing or changing one of the many plugins slots in [[header->https://github.com/openedx/frontend-component-header/tree/master/src/plugin-slots]] or [[footer->https://github.com/openedx/frontend-component-footer/tree/master/src/plugin-slots]]
Customising the header or footer is now no different than [[any other slot|plugin-slots]].# Creating a New Plugin Slot
Creating a new Plugin Slot is rather easy. All you need to do is wrap the component you want to replace in a `PluginSlot` component imported from `@openedx/frontend-plugin-framework`.
It doesn't even need to wrap a component. If you want to be able to insert an item in a list or a widget in a sidebar, the slot can simply be empty and enable inserting content where it's placed.
The bare minimum a PluginSlot needs to have is an `id`. Slot IDs are generally in the format "org.openedx.frontent.MFE_NAME.COMPONENT.v1". For instance the slot id for the course list in the learner dashboard MFE is "org.openedx.frontend.learner_dashboard.course_list.v1". There may also be a few v2 components around, as generally the idea is to update the version if there is an incompatible new slot for the same component.
You might also see `idAliases` for some slots which is to list alternate IDs. This is to allow renaming a slot while still allowing it to keep working. In the initial days of slots before the format was finalised the slot names were simpler. For example the above coruse list slot had an id of "course_list_slot" which could cause issues if there were multiple components that were listing courses.
Another important attribute of `PluginSlot` is `pluginProps` which is to pass any props that need to be passed to the plugin. The items listed here should be the bare minimum needed for that slot to function. If ther are other ways for the slot to get the information then it's best to leave it out. The reason for this is that anything added here becomes part of the API and changes to it could break a future component.
One final convention to about slots is that all of them should be extracted out into the `src/plugin-slots` folder of the MFE. Let's [[look at an example-->plugin-slot-example]] to see how this would work.
# Plugin Slots
MFEs now support an extension mechanism called Plugin Slots. These are parts of an MFE that are designed to be customisable or replaceable.
Let's take a simple example: the course card on the learner dashboard.
{embed image: './images/course-card.png'}
By default, the learner dashboard displays a list of courses, each in the form of a card with the name of the course, a dropdown with actions, one or more action buttons to begin or upgrade the course, and banners below the course.
If a client wanted us to add a button for each course that took students to a page on their CMS about that course, we could create a plugin that would fit in [[this slot->https://github.com/openedx/frontend-app-learner-dashboard/tree/master/src/plugin-slots/CourseCardActionSlot]] and add that button here.
What if instead the client wanted us to make the list of courses into a table of courses with columns? We could then implement a component listing the courses in a table and put it in the [[CourseListSlot->https://github.com/openedx/frontend-app-learner-dashboard/tree/master/src/plugin-slots/CourseListSlot]] instead.
However, what if the client wanted us to change the contents of the action dropdown or move the logo to the right side? There is currently no slot to replace just the course card; we could either:
* Replace the entire course listing with a custom listing using our custom card component.
* [[Create a new slot that allows us to make the extensions we want->new-plugin-slot]]
The correct approach to use here really depends on the situation. Replacing a large and complex component can introduce a risk of code drift. The component in the platform will keep getting updates and potentially new features, whereas our custom component would either lag behind or need to be kept up to date.
If the only way to make the changes you want is to replace a huge component just to make changes to a child component, it might be better to [[introduce a new slot->new-plugin-slot]] instead.# Example Plugin Slot
Continuing from the [[previous example->plugin-slots]] where we wanted to add a new plugin slot for the course card in the learner dashboard MFE, here is what we'd need to do:
1. Find out where all the component is used. If it is used in multiple places, it will require multiple slots one for each place it's used. In this case there is a single usage [[here->https://github.com/openedx/frontend-app-learner-dashboard/blob/2a0ed5714f5e269ac138302b1032ce3dcf875631/src/containers/CoursesPanel/CourseList/index.jsx#L26]].
2. Create a new folder in `src/plugin-slots` with the name of the slot component(s). Here it could be `CourseCardSlot`.
3. Each slot should have a readme file that explains where the slot goes, and contain one or more working code samples along with a screenshot of how they work. You can look at [[any other slot->https://github.com/openedx/frontend-app-learner-dashboard/tree/master/src/plugin-slots/CourseCardActionSlot]] to see how this is formatter
4. The plugin slot component will simply containg a PluginSlot with a unique ID, appropriate props and have the original component inside it.
5. You can now replace the usage of the original component with the plugin slot you just created.
Now this component is ready to be altered by a plugin!# Theming the Platform with Simple Theme
Simple Theme has gone through major changes recently that pivot it away from legacy theming mechanisms like comprehensive theming (which only ever worked for edx-platform and is of minimal relevance now) and branding packages (which are supported in Teak but not beyond). It is now focussed on Design Tokens which is the latest mechanism for theming the platform.
If you want to know more about design tokens themselves you can read more extensive guide written for them [[here->https://doc.opencraft.com/theming-with-design-tokens/]]. Generally you shouldn't need to mess with them directly since Simple Theme should handle most cases.
Simple Theme has two components, [[edx-simple-theme->https://github.com/open-craft/edx-simple-theme]] which will be built and installed onto the MFEs, and the [[simple theme Tutor plugin->https://gitlab.com/opencraft/dev/tutor-contrib-grove/-/tree/main/tutorgrove/plugins/simple_theme]] which is a part of the `tutor-contrib-grove` collection of Tutor plugins.
Initially the way Simple Theme worked was that you'd set up theme configuration in a configuration files and deploy it using the (now deprecated) `edx/configuration` ansible playbooks. These could be confiured via OCIM, and with the switch to Grove they could be configured in the Grove config file.
The latest approach is to directly use a Tutor plugin such that it will work with Grove or whatever future mechanism we use that is based on Tutor.
For most common usecases we want to just confiure a few key colors like the brand, primary and secondary colors. This is very easy with the Simple Theme Tutuor Plugin. You can read about how to use it [[here->https://gitlab.com/opencraft/dev/tutor-contrib-grove/-/tree/main#theming-configuration]].
If what you want change isn't supported by Simple Theme, you should try the following in order:
> [[Add the new design token upstream->create-new-desgn-token]]
> [[Add the new token to simple-theme->add-new-token-to-simple-theme]]
> [[Add the change to a simple-theme fork->client-fork-of-simple-theme]]
# Creating a new Design Token
Creating a new Design Token will often involve two PRs, one to the MFE and the other to Paragon. Let's take an example.
One feature that Simple Theme supports, but could be a nice thing to upstream is the ability to change the color of the sign-in and registration buttons separate from other buttons. We do this by overriding the styles of this button using a new token used only in Simple Theme (more on that [[here->add-new-token-to-simple-theme]].
If we wanted to upstream this, we would make one PR to the [[Authn MFE->https://github.com/openedx/frontend-app-authn/]], adding a new class to the login and registration buttons and using the new tokens to style these buttons. The second PR would be made to [[Paragon->https://github.com/openedx/paragon/]] adding this new design token with description to the correct file in the [[tokens directory->https://github.com/openedx/paragon/tree/release-23.x/tokens/src]] with a description and a fallback value that matches the current value.
This is quite similar to creating a new [[plugin slot->new-plugin-slot]] in that it allows customising a new aspect of the platform.
**Quick Note:** If you create a new token at `color.sign-in.btn.bg` then it will be output to a css variable called `--pgn-color-sign-in-btn-bg`.
> [[Back to Simple Theme Customisation->simple-theme]] # Adding a new Design Token to Simple Theme
If adding a new design token upstream isn't viable, perhaps because the feature isn't something that is desirable upstream but would be useful for users of simple theme, you can add the token to Simple Theme instead.
You can write the CSS in the _overrides.scss file in Simple Theme and create the corresponding design token in the tokens folder.
For instance theming the login and registration buttons separate from other buttons might not be something upstream wants, but we can override the styles of those buttons in Simple Theme and create the corresponding tokens in tokens files in Simple Theme.
> [[Back to Simple Theme Customisation->simple-theme]] # Add a Feature to a Client Fork
If the featre is too sepcific to a client and doesn't fit either upstream or in Simple Theme without impacting other clients/users, then the only way is to create a fork of Simple Theme for that client, and placing any customisations in the _customize.scss file.
Note that even when implementing stuff for a single client it might make sense to use design tokens instead of hardcoding values since it will allow making variable adjustment via a redeployment or theme update rather needing to update any code.
> [[Back to Simple Theme Customisation->simple-theme]]