Skip to main content

Atoms Augmentation

Bento has been built following the principles of Atomic CSS. You can read more about it in the dedicate section.

This means Bento defines a set of "atomic properties", which correspond to single-purpose CSS classes. This classes, besides being used internally, can be also used to build new custom components for your project.

In addition to this, Bento allows you to extend the default atomic properties to meet your project's requirements. This process uses the Vanilla Extract's Sprinkles API, which we mentioned above.

In Vanilla Extract terminology, the atoms are called sprinkles. Bento defines its own sprinkles and it exposes them as bentoSprinkles (we've seen in the examples above).

As discussed in the Atomic CSS documentation, bentoSprinkles is a function which returns a class name given a set of known properties.

Bento exports a Box component, which you can think as the JSX equivalent of a sprinkles function.

info

Box is the main building block of the design system. You can use it to build your own components without accessing sprinkles directly:

Think of Box as a drop-in replacement for div (even though you can customize the rendered HTML element using the as props) which also exposes the type-safe API of sprinkles.

By default, Box uses the default set of sprinkles defined by Bento (bentoSprinkles)

Suppose now you want to add a new color token (specialBackgroundColor) to the set of possible background colors.

To do so, we must extend the set of tokens accepted by the background CSS properties to include also the custom token.

First of all, in order to hook into the default Bento sprinkles, we need to add @vanilla-extract/css and @vanilla-extract/sprinkles as dependencies:

pnpm add -D @vanilla-extract/css @vanilla-extract/sprinkles

Then let's define the new CSS variable for the custom token and assign the corresponding hex color value to it:

my-project/design-system/src/theme.css.ts
import { createGlobalTheme } from "@vanilla-extract/css";
import { vars } from "@buildo/bento-design-system";

// The implementation of Bento's theme we've seen above
createGlobalTheme(":root", vars, {
fontFamily: {
default: "Arial",
},
// ...
});

// our new custom variables
export const customVars = createGlobalTheme(":root", {
color: {
specialBackgroundColor: "#3C6FD6",
},
});

Now we must tell Bento the new token can be used as a value for the background CSS property.

To do so, we will define our own sprinkles function, adding the new token to the set of possible values for the background property. We can leverage the default values exported by Bento and augment it with our tokens:

my-project/design-system/src/sprinkles.css.ts
import { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";
import {
unconditionalProperties,
unconditionalPropertiesShorthands,
responsiveProperties,
responsivePropertiesConditions,
responsivePropertiesDefaultCondition,
responsivePropertiesShorthands,
statusProprties as bentoStatusProperties,
statusPropertiesConditions,
statusPropertiesDefaultCondition,
} from "@buildo/bento-design-system";

import { customVars } from "./theme.css";

// Define our own status properties by extending the default ones
const statusProperties = {
...bentoStatusProperties,
background: {
...bentoStatusProperties.background,
...customVars.background,
},
};

const unconditionalStyles = defineProperties({
properties: unconditionalProperties,
shorthands: unconditionalPropertiesShorthands,
});

const responsiveStyles = defineProperties({
conditions: responsivePropertiesConditions,
defaultCondition: responsivePropertiesDefaultCondition,
properties: responsiveProperties,
shorthands: responsivePropertiesShorthands,
});

const statusStyles = defineProperties({
conditions: statusPropertiesConditions,
defaultCondition: statusPropertiesDefaultCondition,
properties: statusProperties,
});

export const sprinkles = createSprinkles(unconditionalStyles, responsiveStyles, statusStyles);
What are statusProperties?

Bento defines 3 different sets of properties: unconditionalProperties, responsiveProperties and statusProperties:

  • unconditionalProperties are CSS properties that don't depend on any condition;
  • responsiveProperties are CSS properties whose value can be set based on the current breakpoint (mobile, tablet, desktop, wide);
  • statusProperties are CSS properties that can be set per-status (default, hover, focus, active, disabled).

The background CSS property is part of the statusProperties, since we can specify a different background color based on the current status. For example, we can specify different colors for the default and hover state in this way:

Then, we create a BentoProvider with your new sprinkles and inform TypeScript of the type of our new sprinkles function:

my-project/design-system/src/index.ts
import { createBentoProvider, PartialBentoConfig } from "@buildo/bento-design-system";
import { mySprinkles } from "./mySprinkles.css";

const config: PartialBentoConfig = {}; // empty config, just for the sake of the example

export const BentoProvider = createBentoProvider(config, mySprinkles);

declare module "@buildo/bento-design-system" {
interface TypeOverrides {
// inform TypeScript about your own sprinkles function
SprinklesFn: typeof sprinkles;
}
}
info

The module declaration above is necessary in order to "override" the default type of sprinkles, so that TypeScript knows about your custom tokens.

It can be defined "inline" in the index file, or in a separate .d.ts file (e.g. bento.d.ts).

Now we can use "specialBackgroundColor" as a value for the background prop of Box:

my-project/design-system/src/HelloWorld.tsx
import { Box } from ".";

export function HelloWorld() {
return <Box background="specialBackgroundColor">The background is "specialBackgroundColor"</Box>;
}

Note that this is still checked by TypeScript: background now accepts all the values defined by Bento plus the custom token you just defined, and it will complain if you pass anything else to it.