Basic
MetaFox Theme system is based on Material-UI (opens in a new tab). A ThemeProvider component is created and you are able to access all standard Material-UI
features in MetaFox platform.
Theming
Theming is the ability to systematically customize site look & feel to reflect better your product's brand such as color, spacing, round corner, shadow, background and typography.
MetaFox provides MaterialUI ThemeProvider
at RootContainer, you can access to add new variables to Theme Provider.
Theme Custom
You can create a custom theme to customize variable and structure layout.
Example for flatten theme:
Create theme flatten:
/**
* @type: theme
* name: flatten
* system: true
* bundle: web
*/
import styles from './styles.json';
import processors from './processors';
const config = {
originBlockLayouts: require('./layout.blocks.origin.json'),
originGridLayouts: require('./layout.grids.origin.json'),
originItemLayouts: require('./layout.items.origin.json'),
originNoContentLayouts: require('./layout.noContents.origin.json'),
originTemplates: require('./layout.templates.origin.json'),
originPageLayouts: require('./layout.pages.origin.json'),
originIntergrationLayouts: require('./layout.intergration.origin.json'),
originSiteBlocks: require('./layout.siteBlocks.origin.json'),
originSiteDocks: require('./layout.siteDocks.origin.json'),
originSiteFixedDocks: require('./layout.siteFixedDocks.origin.json'),
blockLayouts: require('./layout.blocks.json'),
gridLayouts: require('./layout.grids.json'),
itemLayouts: require('./layout.items.json'),
noContentLayouts: require('./layout.noContents.json'),
templates: require('./layout.templates.json'),
pageLayouts: require('./layout.pages.json'),
siteBlocks: require('./layout.siteBlocks.json'),
pageSizesPriority: {
medium: ['large']
},
processors,
styles
};
export default config;
You can pass a custom CSS for custom theme
Component custom
// packages/metafox/theme-flatten/src/processors/Components.ts
import { Theme } from '@mui/material';
export default function overridesComponents(theme: Theme): void {
// https://v4.mui.com/api/button/#css
if (!theme.components) {
theme.components = {};
}
theme.components.MuiTruncateText = {
defaultProps: {
component: 'div'
}
};
// https://mui.com/api/form-helper-text/#css
theme.components.MuiFormHelperText = {
styleOverrides: {
root: {
fontSize: '13px',
color: theme.palette.grey['500'],
whiteSpace: 'pre-wrap',
margin: 0,
marginTop: '6px'
}
}
};
theme.components.MuiSkeleton = {
variants: [
{
props: { variant: 'avatar' },
style: { borderRadius: '50%' }
},
{
props: { variant: 'button' },
style: { borderRadius: 4 }
},
{
props: { variant: 'icon-button' },
style: { borderRadius: '50%' }
}
]
};
theme.components.MuiDialog = {
styleOverrides: {
paper: {
backgroundImage: 'none',
transition: 'max-height 1s ease-in-out'
}
},
variants: [
{
props: { variant: 'alert' },
style: {
'& .MuiDialogContent-root': {
paddingTop: `${16}px !important`
}
}
}
]
};
}
Css custom
import { Theme } from '@mui/material';
export default function overridesGlobalStyles(theme: Theme) {
if (!theme.components) {
theme.components = {};
}
theme.components.MuiCssBaseline = {
styleOverrides: {
html: {
WebkitFontSmoothing: 'auto',
fontSize: '16px'
},
body: {
fontFamily: theme.fontFamily,
overflowX: 'hidden',
fontSize: '0.8125rem',
WebkitTapHighlightColor: 'transparent',
[theme.breakpoints.up('md')]: {
minHeight: '100vh'
}
},
a: {
color: theme.palette.primary.main
}
}
};
}
styled
You can write custom styles by using styled
, makeStyles
, creatStyles
features (Material UI (opens in a new tab)), look like the below example:
import { styled, Tooltip } from '@mui/material';
import HtmlViewer from '@metafox/html-viewer';
import React from 'react';
const name = 'BlogItemView';
const FlagWrapper = styled('span', {
slot: 'FlagWrapper',
name
})(({ theme }) => ({
display: 'inline-flex'
}));
Control className logic
You can control React className properties by classnames
or clsx
tools. clsx
is a choice of Material-UI
. Thus, we should not add others tools for the same features.
For more info, you can check trends (opens in a new tab) and clsx (opens in a new tab)
Variables
Declare variables for theme
{
"default": {
"searchFormWidth": 320,
"fontFamily": "system-ui, -apple-system, system-ui, \"Segoe UI\", \".SFNSText-Regular\", Roboto, Helvetica, \"sans-serif\"",
"breakpoints": {
"values": {
"xs": 0,
"sm": 720,
"md": 1024,
"lg": 1281,
"xl": 1920
}
},
"block": {
"width": "250px"
},
"middleBlock": {
"maxWidth": "720px"
},
"mixins": {},
"typography": {
"fontFamily": "system-ui, -apple-system, system-ui, \"Segoe UI\", \".SFNSText-Regular\", Roboto, Helvetica, \"sans-serif\"",
"htmlFontSize": 16,
"fontSize": 14,
"fontWeightLight": 300,
"fontWeightRegular": 400,
"fontWeightMedium": 500,
"fontWeightSemiBold": 600,
"fontWeightBold": 700,
"h1": {
"fontWeight": 700,
"fontSize": "40px",
"lineHeight": 1.2,
"letterSpacing": "0"
},
"h2": {
"fontWeight": 700,
"fontSize": "32px",
"lineHeight": 1.25,
"letterSpacing": "0"
},
"h3": {
"fontWeight": 700,
"fontSize": "24px",
"lineHeight": 1.35,
"letterSpacing": "0"
},
"h4": {
"fontWeight": 700,
"fontSize": "18px",
"lineHeight": 1.3333333333333333,
"letterSpacing": "0"
},
"h5": {
"fontWeight": 700,
"fontSize": "15px",
"lineHeight": 1.3333333333333333,
"letterSpacing": "0"
},
"h6": {
"fontWeight": 700,
"fontSize": "13px",
"lineHeight": 1.3076923076923077,
"letterSpacing": "0"
},
"subtitle1": {
"fontWeight": 700,
"fontSize": "18px",
"lineHeight": 1.3333333333333333,
"letterSpacing": "0"
},
"subtitle2": {
"fontSize": "16px",
"lineHeight": 1.5
},
"body1": {
"fontSize": "15px",
"lineHeight": 1.33
},
"body2": {
"fontSize": "0.8125rem",
"lineHeight": 1.33
}
},
"gutter": 16,
"shape": {
"borderRadius": 0
},
"blockShadow": "none",
"avatarSize": {
"medium": 48,
"small": 32
},
"appBarHeight": {
"normal": 58,
"fixed": 1
},
"popoverWidth": {
"appbar": 360
},
"gridPoint": 8,
"nav": {
"background": "#fff",
"color": "#828080",
"menuColor": "red",
"menuBgHover": "#aeacac",
"textColor": "#050505",
"searchColor": "#828080"
},
"palette": {
"default": {
"main": "#050505",
"light": "#050505",
"dark": "#050505",
"contrastText": "#fff"
},
"primary": {
"main": "#2682d5",
"light": "#4a97dc",
"dark": "#0a71cd"
},
"success": {
"main": "#31a24a",
"light": "#50b967",
"dark": "#198b32"
},
"warning": {
"main": "#f4b400",
"light": "#ffca34",
"dark": "#c08e00"
},
"error": {
"main": "#f02848",
"light": "#f34e68",
"dark": "#d20425"
},
"info": {
"main": "#0288d1",
"light": "#03a9f4",
"dark": "#01579b"
},
"secondary": {
"main": "#050505",
"light": "#2d2d2d",
"dark": "#050505"
},
"grey": {
"50": "#fafafa",
"100": "#f5f5f5",
"200": "#ededed",
"300": "#e0e0e0",
"400": "#bdbdbd",
"500": "#aeacac",
"600": "#828080",
"700": "#616161",
"800": "#494949",
"900": "#2d2d2d",
"A100": "#212121",
"A200": "#191919",
"A400": "#0d0d0d",
"A700": "#050505"
},
"border": {
"primary": "#2682d5",
"secondary": "#ededed",
"outlined": "transparent"
},
"background": {
"default": "#ededed",
"paper": "#fff",
"secondary": "#ededed"
},
"appBar": "#fff",
"divider": "#ededed",
"action": {
"active": "rgba(0,0,0,0.54)",
"hover": "rgba(0,0,0,0.04)",
"hoverOpacity": 0.04,
"selected": "rgba(0,0,0,0.08)",
"selectedOpacity": 0.08,
"disabled": "rgba(0,0,0,0.26)",
"disabledBackground": "rgba(0,0,0,0.12)",
"disabledOpacity": 0.38,
"focus": "rgba(0,0,0,0.12)",
"focusOpacity": 0.12,
"activatedOpacity": 0.12
},
"button": {
"hover": "rgb(38, 132, 214, 0.04)"
},
"text": {
"primary": "#050505",
"secondary": "#616161",
"disabled": "#bdbdbd",
"hint": "#aeacac"
}
},
"layoutSlot": {
"background": {
"paper": "#f5f5f5"
},
"points": {
"xs1": 306,
"xs2": 322,
"xs3": 400,
"xs": 400,
"sm1": 600,
"sm": 720,
"md": 1024,
"lg": 1200,
"xl": 1920
}
},
"blockDivider": {
"fullWidth": {
"display": "block",
"margin": "0",
"height": "1px",
"background": "#ededed"
},
"middle": {
"display": "block",
"margin": "0 16px",
"height": "1px",
"background": "#ededed"
}
},
"headerSearchForm": {
"width": 512,
"root": {
"width": 512,
"left": 0,
"position": "absolute",
"transform": ""
},
"form": {
"width": 480
}
},
"siteBackground": "#eef2f4 fixed 0 0"
},
"dark": {
"palette": {
"default": {
"light": "#fff",
"main": "#fff",
"dark": "#fff",
"contrastText": "#fff"
},
"border": {
"primary": "#434a55",
"secondary": "#4b5360",
"outlined": "#525b69"
},
"background": {
"default": "#2d2d2d",
"paper": "#434a55",
"secondary": "#2d2d2d"
},
"appBar": "#434a55",
"divider": "#4b5360",
"action": {
"active": "rgba(255, 255, 255, 0.54)",
"hover": "rgba(255, 255, 255, 0.08)",
"hoverOpacity": 0.08,
"selected": "rgba(255, 255, 255, 0.16)",
"selectedOpacity": 0.16,
"disabled": "rgba(255, 255, 255, 0.26)",
"disabledBackground": "rgba(255, 255, 255, 0.12)",
"disabledOpacity": 0.38,
"focus": "rgba(255, 255, 255, 0.12)",
"focusOpacity": 0.12,
"activatedOpacity": 0.24
},
"button": {
"hover": "rgba(38, 130, 213, 0.08)"
},
"text": {
"primary": "#fff",
"secondary": "#cecece",
"disabled": "#bdbdbd",
"hint": "#828080"
},
"error": {
"main": "#f34e68"
}
},
"blockDivider": {
"fullWidth": {
"background": "#828080"
},
"middle": {
"background": "#828080"
}
},
"siteBackground": "#545d6b fixed 0 0"
}
}
Dark mode vs Light mode
Metafox comes with two palette modes: light (the default) and dark.
As example above, we have "dark" property for define custom variable for dark mode.
Typescript
In other to extend declarations, please read Customization of theme (opens in a new tab) and Package Augmentation (opens in a new tab)
Hook
You can use useTheme
in pure function to get theme variables as the below example:
import { useTheme } from "@metafox/framework/theme";
export const MyComponent = () => {
const theme = useTheme();
return (
<span
style={{ background: theme.status.background, color: theme.status.color }}
>
Using Theme Variable
</span>
);
};
HOC
Example:
import { withTheme } from "@metafox/framework/theme";
function DeepChildRaw(props) {
const theme = useTheme();
return (
<span
style={{ background: theme.status.background, color: theme.status.color }}
>
Using Theme Variable
</span>
);
}
const DeepChild = withTheme(DeepChildRaw);