Styling
Refer to the styling example project for working examples of supported methods of styling your components, including a number of major styling libraries.
Tailwind
You can configure Tailwind for your components by doing the following:
- Add a dependency to
tailwindcss
to yourpackage.json
- Add a PostCSS configuration file to your project subcomponent including tailwind as a plugin
- Refer to a Tailwind config in that plugin with any Tailwind-specific configuration
- Import a CSS file in your top level component with the base Tailwind layer directives
See the TailwindPartial
component and the relevant configuration in the example for more detail.
styled-components
You can use styled-components
in your project by doing the following:
- Add a
dependency
onstyled-components
to yourpackage.json
- Create a registry component using the styled-components server side rendering API along with
useInlineHeadAsset()
and wrap the components you intend to style in it. The example includes aStyledComponentsRegistry.jsx
you can use. - For each Island usage, you must wrap each subtree in a registry to capture styles when rendering on the server. To make this easier you may use the
Wrapper
prop on theIsland
component to wrap the contents without needing to edit the island components themselves. Note that This prop also lets you configure this once by replacing all instances of<Island />
with a<StyledIsland />
that looks something like:
Important
when using the Wrapper
prop you must import the component passed with a ?client
suffix to make sure it can be bundled for the client.
import { Island } from '@hubspot/cms-components';
import StyledComponentsRegistry from './StyledComponentsRegistry?client';
export default function StyledIsland(props) {
return <Island {...props} Wrapper={StyledComponentsRegistry} />
}
import { Island } from '@hubspot/cms-components';
import StyledComponentsRegistry from './StyledComponentsRegistry?client';
export default function StyledIsland(props) {
return <Island {...props} Wrapper={StyledComponentsRegistry} />
}
- You can now
import styled from 'styled-components';
and use it to style your components.
styled-jsx
Steps to use styled-jsx
are:
- Add a dependency on
styled-jsx
to yourpackage.json
- Create a registry component using the server side rendering API and
useInlineHeadAsset()
. The example includes aStyledJSXRegistry.jsx
to refer to or use. - The registry component for
styled-jsx
must also be wrapped on anyIsland
usage to prevent hydration mismatches or missing styles on initial load. Note thatstyled-jsx
's implementation depends onuseId()
hooks in a way that can cause mismatches if not properly configured. See theStyledJSXIsland.jsx
implementation from the example for an easier pattern of replacing all direct<Island />
usage. - You can now use
styled-jsx
to style your components, including<style jsx>{` /* CSS here */ `}</style>
patterns.
CSS Modules
You can use CSS Modules within any React components by importing a file ending in .module.css, which will return a CSS module object:
/example.module.css */
.red {
color: red;
}
/How to have global—non-namespaced—styles in CSS modules */
:global(html) {
border: 6px solid SteelBlue;
}
/example.module.css */
.red {
color: red;
}
/How to have global—non-namespaced—styles in CSS modules */
:global(html) {
border: 6px solid SteelBlue;
}
import classes from './example.module.css';
export default function MyComponent() {
return <div className={classes.red}>red text</div>;
}
import classes from './example.module.css';
export default function MyComponent() {
return <div className={classes.red}>red text</div>;
}
When you important a CSS modules file from inside a React component:
- A
<style>
tag will automatically be inserted into the page for you when the component is server-rendered OR when it is dynamically rendered on in the browser - Those styles will automatically be namespaced so they don’t interfere with anything else on the page
Note, you can also import regular CSS files into your React components. But their selectors will not be automatically namespaced.
Dynamic styles based on props
If you need to dynamically adjust styles based on props, here are some options:
- If you have some conditional style that is either on or off, then you can have a className that the React component code conditionally renders in your JSX.
- However if you have some dynamic style that is not a toggle but rather a specific color or number that you need to apply to your styles then you can:
- Define CSS custom properties in your CSS or CSS modules code and inject new CSS custom property values via React
- Use React to set inline styles on the specific part of the module HTML needed
Here’s a hypothetical example of all three of those techniques in action:
/example2.module.css */
.fancy-module-wrapper {}
.purple-border {
border: 2px solid rebeccapurple;
}
.second-text {
color: var(--second-text-color, mediumvioletred);
}
/example2.module.css */
.fancy-module-wrapper {}
.purple-border {
border: 2px solid rebeccapurple;
}
.second-text {
color: var(--second-text-color, mediumvioletred);
}
import styles from './example2.module.css';
export default function FancierComponent(props) {
const { hasPurpleBorder, paddingPx, customSecondTextColor } = props;
// Example: toggling styles via a prop
const classes = [styles['fancy-module-wrapper']];
if (hasPurpleBorder) {
classes.push(styles['purple-border']);
}
// Example: using inline style attribute (with React's style syntax)
const inlineStyles = { padding: paddingPx };
// Example: setting a CSS custom property value that's picked up by other CSS
const inlineAndCustomPropertyStyles = {
...inlineStyles,
'--second-text-color': customSecondTextColor,
};
return (
<div className={classes.join(' ')} style={inlineAndCustomPropertyStyles}>
<p>First text</p>
<p className={styles['second-text']}>Second text</p>
</div>
);
}
import styles from './example2.module.css';
export default function FancierComponent(props) {
const { hasPurpleBorder, paddingPx, customSecondTextColor } = props;
// Example: toggling styles via a prop
const classes = [styles['fancy-module-wrapper']];
if (hasPurpleBorder) {
classes.push(styles['purple-border']);
}
// Example: using inline style attribute (with React's style syntax)
const inlineStyles = { padding: paddingPx };
// Example: setting a CSS custom property value that's picked up by other CSS
const inlineAndCustomPropertyStyles = {
...inlineStyles,
'--second-text-color': customSecondTextColor,
};
return (
<div className={classes.join(' ')} style={inlineAndCustomPropertyStyles}>
<p>First text</p>
<p className={styles['second-text']}>Second text</p>
</div>
);
}
Other CSS-in-JS libraries
Other CSS-in-JS libraries that provide a server side rendering API and don't depend on a Babel plugin can be used within HubSpot projects. The same registry pattern described above can be generalized for other libraries to emit CSS to include as part of the server render. The registry will need to be included as a Wrapper
on any <Island />
usage as well if there are styles within the island.