Before Customizing
Assess the Change
Before anything, evaluate the request or issue to understand its complexity. Could it be something that can be easily addressed via the Drupal settings? This is the most non-invasive way to make changes, so always check this first.
Design Decisions
If the desired change is more involved than just a settings tweak, discuss the following options with your designer:
- Ignore: If the issue or change request is minor or not crucial to the website's main functionality or user experience, you might consider ignoring it.
- Adjust Design to Match Existing Functionality: If the issue pertains to a design feature that doesn't match current functionality, then adjust the design to match what the platform can currently do. This way, you're not spending extra development hours trying to fit a square peg in a round hole.
- Opt for Alternate Functionality: Sometimes the desired outcome can be achieved using a different set of tools or functionality that Gesso already supports. This may involve a slight detour from the original plan, but it can be a worthwhile compromise to keep things streamlined and within scope.
- Use Existing Customization Options: Tokens, Props, Presets and Defaults are all acceptable changes for any project, even a Gesso Base project.
Collaboration with the Gesso Team
If after your assessment, you believe there might be an inherent issue or a potential enhancement with the Gesso theme:
- Bug Reporting: If you identify something that appears to be a bug in the Gesso theme, relay that information to the Gesso team.
- Feature Request: If during your project, you discover a potential enhancement or feature that could be beneficial for Gesso, submit it as a feature request. However, be prepared to adopt one of the interim solutions (ignoring the issue, adjusting the design, etc.) until the feature gets implemented in a new release.
Customizing Components
Components in React can be customized and extended in various ways to better suit your specific needs. This flexibility comes from several methods including the use of tokens, props, presets, default settings, Higher Order Components (HOCs), and custom components. Here's a detailed look into each of these methods:
Tokens
Tokens allow you to customize components indirectly. These are a collection of reusable properties that describe certain aspects of a design. For instance, consider a basic button, which as no customization directly, can be changed significantly by changing the tokens in Figma:
<Button>Click Me</Button>
Props
Props offer extensive customization options. They come with a multitude of settings that allow you to adjust the aesthetics and functionality of your components. Here's an example:
<Button variant="outlined" color="primary" size="large">
Click Me
</Button>
Special Props
Gesso is just starting to expose certain standard props to allow for additional customization. Please note not all components currently support this functionality.
tokens
All tokens usage for a component is exposed via the tokens object, which is a key/value pair of all token usages
childProps
All childProps usage for a component is exposed via the childProps object, which is a key/value pair of all childProps usages
Presets
Presets enable you to store a collection of props within an object for reuse, making your component creation process more efficient. Here's an example of a preset used for a button:
const A1Button = {
variant: "outlined",
color: "primary",
size: "large",
children: "Click Me",
};
return <Button {...A1Button} />;
Defaults
The Gesso library lets you set default properties for components within your theme. This is useful for defining defaults for properties that aren't supported by tokens. Note, this isn't universally supported across all components.
Here's an example of:
- Importing custom Figma tokens into your theme.
- Setting default properties for the Button component.
- Setting default typography properties for the theme's H1 styles.
import { createGessoTheme } from '@acromedia/gesso';
import colors from '../../tokens/colors';
import fontFamilies from '../../tokens/fontFamilies';
const secondaryFontFamily = fontFamilies.secondary;
const theme = createGessoTheme(
// Figma tokens.
{
colors,
fontFamilies,
},
// Component defaults.
{
Button: {
variant: "outlined",
color: "primary",
size: "large",
children: "Click Me",
},
},
// Typography defaults.
{
h1: {
fontFamily: secondaryFontFamily,
},
h1Small: {
fontFamily: secondaryFontFamily,
},
},
);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<Button />
</ThemeProvider>
</StyledEngineProvider>
);
Token Overrides
Token overrides are a key feature in the Gesso theme that provide a layer of customization to your components. They enable you to override specific design tokens, allowing for a more granular control over the styling of components. This is particularly useful when the default design tokens do not align with your design requirements.
How to Use Token Overrides
Token overrides are implemented through a function that retrieves the override value for a specific token. This function takes the token's name and a default value as parameters. The token's name corresponds to a specific property in the theme's design system, and the default value is used if the token override is not provided.
For example:
'& .comp-cart_empty-cart': {
width: getTokenOverrideValue('empty.width', '92%'),
height: getTokenOverrideValue('empty.height', '97%'),
[theme.breakpoints.down('md')]: {
width: getTokenOverrideValue('empty.breakpointDownMd.width', '100%'),
height: getTokenOverrideValue('empty.breakpointDownMd.height', '80%'),
},
},
In this code snippet, getTokenOverrideValue is a function utilized to retrieve the value of a specified design token. If an override for the token exists, the function returns the overridden value; otherwise, it defaults to the specified fallback value. For example, the token 'empty.width' looks up the width for the .comp-cart_empty-cart class. If an override is defined in the theme tokens for 'empty.width', that value is used; otherwise, the width defaults to '92%'.
This approach also accommodates responsive design needs. For instance, within the theme.breakpoints.down('md') section, different token values are specified for medium-sized and smaller viewports. Here, if there are overrides for 'empty.breakpointDownMd.width' and 'empty.breakpointDownMd.height', those values are used; if not, it falls back to '100%' and '80%', respectively. This dynamic handling of tokens allows for flexible and responsive styling based on the theme's configuration.
With the changes made to the relevant css file, it is necessary to also add it to the types file. It is important that the css values and the token types match percisely.
export interface CartTokens {
empty?: {
width?: number | string,
height?: number | string,
breakpointDownMd?: {
width?: number | string,
height?: number | string,
},
};
}
UNLESS YOU SPECIFICALLY HAVE BUDGET FOR THIS CUSTOM FUNCTIONALITY, DO NOT USE ANY BELOW STEPS.
Higher Order Components (HOC)
A Higher Order Component (HOC) is a function that takes a component and returns a new component with additional props or behavior. It allows you to reuse component logic, manage prop manipulation, abstract complex hooks logic, or handle conditional rendering.
Here's an example of a HOC that introduces error handling to any component:
function withErrorHandling(Component) {
return function ({ hasError, ...props }) {
if (hasError) {
return <div>Error! Something went wrong.</div>;
}
return <Component {...props} />;
};
}
const ButtonWithErrorHandling = withErrorHandling(Button);
return <ButtonWithErrorHandling hasError={false} />;
In this example, ButtonWithErrorHandling
will display an error message if the hasError
prop is true
.
Custom Components
Custom components are unique components that you create to serve specific functions within your application. They allow for greater flexibility and customization compared to standard components.
For instance, consider a PrimaryButton
component that includes an icon, uses a theme's primary color, and includes loading state handling:
import { styled, Button, Typography } from "@acromedia/gesso";
const styles = ({ theme }) => {
if (!theme) return;
return {
color: theme.palette.primary.main,
backgroundColor: theme.palette.primary.light,
position: "relative",
padding: theme.spacing(2),
display: "flex",
justifyContent: "center",
alignItems: "center",
};
};
const PrimaryButton = ({ isLoading, children, ...props }) => (
<div {...props}>
{isLoading && <span>Loading...</span>}
<Typography variant="button">{children}</Typography>
</div>
);
export default styled(PrimaryButton)(styles);
In this example, PrimaryButton
is a button that uses the primary color defined in the theme, and displays a loading message if the isLoading
prop is true
. This component encapsulates more complex behavior and styling than a typical button.