How to hack around Gestalt
Guidelines for customizing Gestalt components.
Disclaimer
For the vast majority of use cases, please do not use these techniques. Please explore building your custom UI element using our primitive components, like Box, TapArea, Pog, etc first! And feel free to ask us for help!
Gestalt's components enforce the design system by restricting possible usage. This is by design! However, in certain scenarios — experimental usage, usage on internal tools or other non-Pinner/M10n surfaces, etc — it may be necessary to circumvent those restrictions to build the desired experience. With that in mind, here are some common techniques and their trade-offs.
Creating new components
Forking components
When a Gestalt component doesn't quite match the desired design spec, a common idea is to fork the component: copy/paste the component's code into the target repo where it can be modified.
Pro:
- Complete freedom to make whatever changes are desired
Con:
- Duplicate code
- Drift from the original component can make re-integration difficult/impossible
- Heavy maintenance burden: any future updates to Gestalt (changes to color, rounding, etc) will need to be made manually, and will likely lead to a broken/outdated UI in the meantime
- Dark mode and RTL support will need to be handled manually
Alternative: Chat with the Gestalt team about your needs and let's see how we can accommodate them. Often features that we don't support are for accessibility or other reasons — but we're happy to see how we can support you!
Custom components
Custom components can also be made from scratch, using native DOM elements and CSS/SCSS.
Pro:
- Complete freedom to build whatever UI is desired
Con:
- Adds to bundle size with custom stylesheets instead of taking advantage of Gestalt's common styles
- Creates disjointed app feel by not working within the design system
- Heavy maintenance burden: no support from Gestalt team for future updates
- Dark mode and RTL support will need to be handled manually
Alternative: Chat with the Gestalt team about your needs and let's see how we can accommodate them. If we can't officially support your needs, at least use Gestalt primitives (Box, TapArea, etc) when building your custom UI to ensure that your feature is accessible and fits in with the rest of the design system.
Modifications to existing components
Box's dangerouslySetInlineStyle
Box provides an "escape hatch" prop, dangerouslySetInlineStyle
, allowing for styles to be set directly on the component. Similarly,
Icon,
IconButton, and
Pog provide the dangerouslySetSvgPath
to allow for custom icons.
Pro:
- Uses Gestalt components for all non-custom styles needed, taking advantage of our hashed classes for smaller stylesheets, adhering to the design system, and getting future upgrades for free
- We track
dangerouslySetInlineStyle
usage, which informs future changes to Gestalt components
Con:
- Doesn't support pseudo-classes, pseudo-elements, or animations
- Overridden styles will not respond to dark mode or RTL
Alternative: When possible, stick to the styles available on Gestalt components natively. If you need to use a custom style, try to use the corresponding design token instead of a hard-coded value. If your design calls for unsupported styles, please feel free to contact us to chat about design options.
import { Box, Flex } from 'gestalt'; import { TOKEN_COLOR_PINK_FLAMINGLOW_400 } from 'gestalt-design-tokens'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Box dangerouslySetInlineStyle={{ __style: { backgroundColor: TOKEN_COLOR_PINK_FLAMINGLOW_400, }, }} height={100} width={100} /> </Flex> ); }
Wrapping components
Certain components have styles that can be overridden when wrapped by (or wrapped around) other components.
Pro:
- Continues to use Gestalt components for all except the custom styles
Con:
- Your UI may look disjointed with the rest of the design system
- Overridden styles will not respond to dark mode or RTL
Alternative: When possible, stick to the styles available on Gestalt components natively. If your design calls for unsupported styles, please feel free to contact us to chat about design options.
import { Flex, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Text color="error"> <span style={{ fontFamily: 'cursive' }}>Custom text</span> </Text> </Flex> ); }
Refs
Components that accept refs (e.g. Box, Button, etc) can be customized by manipulating the referenced element.
Pro:
- Uses Gestalt components for all non-custom styles needed, taking advantage of our hashed classes for smaller stylesheets, adhering to the design system, and getting future upgrades for free
- Directly targets the instance of the component rather than relying on the underlying DOM elements
Con:
- Doesn't support pseudo-classes, pseudo-elements, or animations
- Typically only targets the outermost element of the component; inner elements of complex components cannot be reached
- Overridden styles will not respond to dark mode or RTL
Alternative: When possible, stick to the styles available on Gestalt components natively. If your design calls for unsupported styles, please feel free to contact us to chat about design options.
import { useEffect, useRef } from 'react'; import { Flex, TextField } from 'gestalt'; export default function Example() { const ref = useRef(null); useEffect(() => { if (ref.current) { ref.current.style.backgroundColor = 'aquamarine'; } }, [ref]); return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <TextField ref={ref} errorMessage="Please don't do this!" id="refExample" onChange={() => {}} readOnly value="Custom color" /> </Flex> ); }
CSS selectors
It is possible to use CSS selectors to peer "under the hood" of Gestalt components and target the underlying DOM elements.
Pro:
- It's CSS, so you can do whatever you want
Con:
- This is very, very brittle and subject to breaking changes at any point; we do not consider breaking changes to the underlying DOM structure when determining
semver so even
patch
changes could break your UI - Overridden styles will not respond to dark mode or RTL
Alternative: Absolutely anything — this is just about the worst way to hack Gestalt components. Your UI will break in the future. Consider using a ref if possible.