Styling Components
Note: This guide applies to PWA 7.32 and later. Starting with 7.32, the app’s styling comes from a configurable theme with dark mode support. See Earlier PWA versions if your extension still uses glamor.
Overview
Section titled “Overview”All colors, typography, border radii and shadows of the app come from one theme object. Merchants configure the theme in the Shopgate admin, and it supports dark mode.
If your extension takes its styling from the theme, it automatically follows the merchant’s settings and dark mode.
Styling with makeStyles
Section titled “Styling with makeStyles”makeStyles from @shopgate/engage/styles gives you access to the theme in your styles. The generated class names are unique, so your styles never collide with the styles of other components.
import { makeStyles } from '@shopgate/engage/styles';
const useStyles = makeStyles()(theme => ({ root: { display: 'flex', background: theme.palette.background.surface, color: theme.palette.text.primary, borderRadius: theme.shape.borderRadius, padding: theme.spacing(2), '&:hover': { background: theme.palette.background.emphasized }, },}));
const MyComponent = ({ className, children }) => { const { classes, cx } = useStyles(); return <div className={cx(classes.root, className)}>{children}</div>;};- Nested selectors start with
&, for example'&:hover'or'& > span'. - Dynamic values: pass them as parameters, e.g.
makeStyles()((theme, { isOpen }) => ({ … }))and thenuseStyles({ isOpen }). - Class components: use
withStyles(Component, theme => ({ … })). The component receivesclassesas a prop. - Theme outside of styles: use
useTheme()from@shopgate/engage/styles.
The most important tokens
Section titled “The most important tokens”| Token | Use for |
|---|---|
palette.background.default |
Page background |
palette.background.surface |
Cards, panels, lists, sheets |
palette.background.emphasized |
Highlighted or selected areas |
palette.text.primary / .secondary |
Main text / secondary text |
palette.primary.main / .contrastText |
Brand color / text on top of it |
palette.secondary.main / .contrastText |
Accent color / text on top of it |
palette.error / warning / success .main |
Status colors |
palette.action.disabled |
Disabled text and icons |
components.border.light / .medium / .dark |
Borders |
components.separatorLine.borderColor |
Dividers |
shape.borderRadius |
Border radius |
shadowSizes.low / .medium / .strong |
Shadows |
layout.safeArea.top / .bottom |
Safe area insets |
The TypeScript type Theme from @shopgate/engage/styles lists all available tokens.
Dark mode
Section titled “Dark mode”As long as you only use theme tokens, dark mode works without any extra code. For the rare exception:
// in makeStyles...theme.applyStyles('dark', { boxShadow: 'none' }),
// in componentsimport { useColorScheme } from '@shopgate/engage/styles';const { activeColorScheme } = useColorScheme(); // 'light' | 'dark'Theme values are CSS variables
Section titled “Theme values are CSS variables”theme.palette.primary.main is not a hex code. It is var(--sg-palette-primary-main). The browser resolves the value, so dark mode and admin changes take effect without a re-render. This has consequences:
- No JS color math. Use the theme helpers
theme.alpha(color, 0.12),theme.darken(color)andtheme.contrastColor(color). - Use
calc()for size tokens. For example`calc(${theme.components.navigator.height} + 8px)`instead ofheight + 8.
Global styles
Section titled “Global styles”To style elements that your extension does not render itself, for example content injected by a third-party script, use injectGlobal from @shopgate/engage/styles. Selectors apply as written.
import { injectGlobal } from '@shopgate/engage/styles';
injectGlobal({ '#provider-chat-button': { bottom: 'calc(var(--footer-height) + 16px)', },});CSS files
Section titled “CSS files”You can create CSS files and import them where you need them. Use the theme’s CSS variables instead of fixed colors:
.my-extension__badge { display: flex; padding: 10px; background: var(--sg-palette-secondary-main); color: var(--sg-palette-secondary-contrastText); border-radius: var(--sg-shape-borderRadius);}import './style.css';
const Badge = ({ children }) => ( <div className="my-extension__badge">{children}</div>);
export default Badge;Inline styles
Section titled “Inline styles”React also supports inline styles. Use them only for values that depend on the component state, and take the values from the theme:
import { useTheme } from '@shopgate/engage/styles';
const Message = ({ error, children }) => { const theme = useTheme(); return ( <div style={{ color: error ? theme.palette.error.main : 'inherit' }}> {children} </div> );};
export default Message;Components
Section titled “Components”- Buttons:
Button,IconButtonandButtonBasefrom@shopgate/engage/components/v2.Buttonsupportsvariant="contained" | "outlined" | "text" | "link"andcolor="primary" | "secondary" | "cta" | ….Buttonfrom@shopgate/engage/componentsis still the old, deprecated button.
- Other components from
@shopgate/engage/components:Typography: text in the theme’s variantsCard: follows the merchant’s card settingPaper: a surface with elevation
Stable class names for merchant CSS
Section titled “Stable class names for merchant CSS”Merchants can restyle the app with their own CSS. Generated class names change with every build. That’s why you should also give stylable elements a fixed, readable class name, e.g. ext-my-extension__panel, and don’t rename it later.
Best practices
Section titled “Best practices”- Take colors only from the theme. No
#fff,'white'or#ccc. - Pick tokens by meaning, not by look:
background.surfacefor content areas,text.secondaryfor secondary text. - Put text on colored areas with
contrastText, so it stays readable with any brand color. - Don’t check the theme name (
isIOSTheme(),themeName). Style through tokens instead. - Test in light and dark mode. In development builds, turn it on in the Development Settings with “Enable Color Scheme Selection”. The route
/shopgate-theme-demoshows all colors, typography variants and buttons.
Earlier PWA versions
Section titled “Earlier PWA versions”Before PWA 7.32, components were styled with glamor, and colors came from themeConfig.colors. Both still work in 7.32, but they don’t follow dark mode or the merchant’s theme settings. Don’t use them in new code, and replace them with makeStyles and theme tokens when you update an existing extension.

