Customizing Patterns
Bamboo provides the ability to customize the built-in patterns, as well as creating your own custom patterns. This is useful to create your own layout pattern abstractions that can be used in your application.
Bamboo allows you to customize built-in patterns and create custom patterns for reusable layout abstractions.
A pattern accepts the following parameters:
description- The description of the pattern.properties- The list of properties that the pattern accepts.defaultValues- The default values for the properties. This is useful when you want to provide a default value for a property.transform- The function that accepts the properties and a set of helpers, and returns a css object.cssProps- Which css properties the pattern accepts beside its own:'all','none', or{ except: [...] }. Can be used to ensure strict typings when using the pattern.strict- Whether to only generate types for the specified properties. This will disallow css properties.
Transform helpers
transform receives a second argument with everything it needs to shape values it cannot inspect itself:
| helper | returns |
|---|---|
map(value, fn) | fn applied to a value, or to each leaf of a conditional one |
token(path, fallback?) | the css variable for a token path, or fallback when the path names no token |
isCssUnit(value) | whether the value is a css length, e.g. 10px |
isCssVar(value) | whether the value is a var(...) reference |
isCssFunction(value) | whether the value is a css function call |
token is what lets a pattern accept either a token name or a raw css value, which is how the built-in bleed, grid
and spacer patterns work:
transform(props, { token, isCssUnit }) {
// '4' → var(--spacing-4) · 'auto' → auto · '10px' → 10px
const value = isCssUnit(props.size) ? props.size : token(`spacing.${props.size}`, props.size)
return { flex: `0 0 ${value}` }
}
Resolve the token here rather than emitting a reference for the css pipeline to read later — the build, the extractor and the browser all answer through this helper, so the value a pattern produces is the same in each.
Creating a Pattern
To create a pattern, you can use the patterns property in the config. Let's say we want to create a "Scrollable"
pattern that applies preset styles to a container that allows for scrolling.
const config = {
patterns: {
extend: {
scrollable: {
description: 'A container that allows for scrolling',
defaultValues: {
direction: 'vertical',
hideScrollbar: true,
},
properties: {
// The direction of the scroll
direction: { type: 'enum', value: ['horizontal', 'vertical'] },
// Whether to hide the scrollbar
hideScrollbar: { type: 'boolean' },
},
// disallow the `overflow` property (in TypeScript)
cssProps: { except: ['overflow'] },
transform(props) {
const { direction, hideScrollbar, ...rest } = props
return {
overflow: 'auto',
height: direction === 'horizontal' ? '100%' : 'auto',
width: direction === 'vertical' ? '100%' : 'auto',
scrollbarWidth: hideScrollbar ? 'none' : 'auto',
WebkitOverflowScrolling: 'touch',
'&::-webkit-scrollbar': {
display: hideScrollbar ? 'none' : 'auto',
},
...rest,
}
},
},
},
},
}
Then you can run the following command to generate the pattern JS code:
pnpm bamboo codegen
Now you can import the pattern and use it in your application:
import { scrollable } from '../styled-system/patterns'
const App = () => {
return (
<div className={scrollable({ direction: 'vertical', hideScrollbar: true })}>
<div>Scrollable content</div>
</div>
)
}
Customizing Built-in Patterns
You can extend the default patterns by using the patterns.extend
property in the config.
bamboo.config.ts
import { defineConfig } from '@bamboocss/dev'
export default defineConfig({
patterns: {
extend: {
// Extend the default `flex` pattern
flex: {
properties: {
// only allow row and column
direction: { type: 'enum', value: ['row', 'column'] },
},
},
},
},
})Then you can run the following command to update the pattern JS code:
pnpm bamboo codegen