Skip to content

Commit 2065d01

Browse files
authored
Merge pull request #3 from cr0cK/feature/README
Add some documentation.
2 parents 34c7f15 + b4d5238 commit 2065d01

File tree

3 files changed

+274
-2
lines changed

3 files changed

+274
-2
lines changed

README.md

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,275 @@
22

33
Single function to create, manage, compose variants, for any CSS-in-JS libraries.
44

5+
6+
## Installation
7+
8+
```bash
9+
npm install build-variants
10+
```
11+
12+
## How to use
13+
14+
### Step 1 - build-variants configuration
15+
16+
To be able to use build-variants with your CSS-in-JS library and get valid typings
17+
for your CSS/styles, you need to specify the type of the CSS object.
18+
19+
To do so, you can create a simple function that expose `newBuildVariants` with
20+
the CSS interface that you want to use.
21+
22+
For example with styled-components:
23+
24+
```ts
25+
import { newBuildVariants } from 'build-variants'
26+
import { CSSObject } from 'styled-components'
27+
28+
/**
29+
* Create a BuildVariants instance, typed to use styled-components's `CSSObject`s.
30+
*/
31+
export function buildVariants<TProps extends object>(props: TProps) {
32+
return newBuildVariants<TProps, CSSObject>(props)
33+
}
34+
```
35+
36+
Note: If your library doesn't expose typings or if you are doing raw CSS only with
37+
React, you can use `React.CSSProperties` for example.
38+
39+
You can even use you own CSS declaration if you want to use build-variants in a
40+
totally different context:
41+
42+
```ts
43+
import { newBuildVariants } from 'build-variants'
44+
45+
// build-variants typings will only tolerate CSS with color and background properties!
46+
interface IMyStyles {
47+
color: string
48+
background: string
49+
}
50+
51+
/**
52+
* Create a BuildVariants instance, typed to use styled-components's `CSSObject`s.
53+
*/
54+
export function buildVariants<TProps extends object>(props: TProps) {
55+
return newBuildVariants<TProps, Partial<IMyStyles>>(props)
56+
}
57+
```
58+
59+
60+
### Step 2 - Build your variants!
61+
62+
63+
```tsx
64+
import { buildVariants } from 'path/to/buildVariants'
65+
66+
// Use here styled-components but you can used any CSS-in-JS library you want
67+
import { styled } from 'styled-components'
68+
69+
// Define the interface of the props used by your component
70+
interface Props {
71+
// Define a 'private' property for the button font color
72+
_color?: 'default' | 'primary' | 'secondary'
73+
74+
// Define a 'private' property for the button background
75+
_background?: 'default' | 'primary' | 'secondary'
76+
77+
// Define a 'private' property for font variants.
78+
// This is an array, meaning that you can apply several values at once.
79+
_font?: Array<'default' | 'bold' | 'italic'>,
80+
81+
type?: 'default' | 'primary' | 'secondary'
82+
}
83+
84+
// Style a div component by using styled-components here.
85+
const Div = styled.div<Props>props => {
86+
// Get a new instance of build-variant.
87+
// Note that we use here `buildVariants()` defined in step 1 to be able to write
88+
// CSS styles as styled-components' CSS objects.
89+
return buildVariants(props)
90+
// Add some CSS.
91+
.css({
92+
background: 'white'
93+
})
94+
95+
// Add more CSS.
96+
// You can add as many CSS blocks as you want.
97+
.css({
98+
'> button': {
99+
all: 'unset'
100+
}
101+
})
102+
103+
// Implement CSS for each case of the color variant.
104+
// Everything is typed checked here. You have to implement all cases of the
105+
// union value.
106+
// Note that because _color is optional, we have to default on a default value,
107+
// here 'default', which is the first value of the union.
108+
.variant('_color', props._color || 'default', {
109+
default: {
110+
// No color for the default case, it will be inherited from a parent
111+
},
112+
113+
primary: {
114+
color: 'white'
115+
},
116+
117+
secondary: {
118+
color: 'black'
119+
}
120+
})
121+
122+
// Same thing with the background variant
123+
.variant('_background', props._background || 'default', {
124+
default: {
125+
// No background override.
126+
// As we have define a background in the first CSS block, we should have
127+
// a background: white at the end.
128+
},
129+
130+
primary: {
131+
background: 'blue'
132+
},
133+
134+
secondary: {
135+
background: 'white'
136+
}
137+
})
138+
139+
// Same thing with the font variant.
140+
// Notice that we use `variants` to manipulate an array of unions.
141+
.variants('_font', props._font || [], {
142+
default: {
143+
// Inherits from the parent
144+
},
145+
146+
bold: {
147+
fontWeight: 'bold'
148+
},
149+
150+
italic: {
151+
fontStyle: 'italic'
152+
}
153+
})
154+
155+
// Now, compose with your 'private' variants
156+
.compoundVariant('type', props.type || 'default', {
157+
// When composing, we get a new instance of the builder to get existing
158+
// private variants definitions.
159+
// Final `end()` function merges all CSS definitions get from the composition.
160+
// Here we don't want to style the default case, so we directly call the
161+
// end function.
162+
default: builder.end()
163+
164+
// Here we define the type=primary variant from existing color, background
165+
// and font variants.
166+
// In this example, we use two definitions for the font variant. So the font
167+
// will be bold and italic.
168+
primary: builder
169+
.get('_color', 'primary')
170+
.get('_background', 'primary')
171+
.get('_font', ['bold', 'italic'])
172+
.end(),
173+
174+
// In the same way, compose to define type=secondary variant.
175+
secondary: builder
176+
.get('_color', 'secondary')
177+
.get('_background', 'secondary')
178+
.get('_font', ['bold', 'italic'])
179+
.end()
180+
})
181+
182+
// You can also compose with an array of compoundVariant by using compoundVariants:
183+
// .compoundVariants('types', props.types || [], {
184+
// ...
185+
// })
186+
187+
// You can conditionate any CSS or variant definition by using `if()` block.
188+
.if(
189+
// Implement the predicate function here to have a pink color in your button
190+
true // OR `props.variants?.include('fancy') === true,
191+
builder => {
192+
return builder
193+
.css({
194+
color: 'pink'
195+
})
196+
197+
// The nice trick with `if` is that this variant can be automatically
198+
// "skipped" in compound variants if
199+
.variant('_color', props._color || 'default', {
200+
// ...
201+
}
202+
203+
.end()
204+
}
205+
}
206+
207+
// The nice trick with `if` is that variants will be automatically "skipped"
208+
// from compound variants when being disabled.
209+
// So your compount variants dont need to have any logic to apply or not
210+
// internal variants, you can conditionnate them outside the composition.
211+
.if(
212+
false,
213+
builder => {
214+
return builder
215+
.variant('_color', props._color || 'default', {
216+
// ...
217+
}
218+
219+
.end()
220+
}
221+
}
222+
223+
// And finally, for various reason, you want to override the color defined
224+
// previously.
225+
// So you can use the `weight` option to ponderate your CSS definition(s).
226+
// Weight is also available for variant(s), compoundVariant(s) and if blocks.
227+
css({
228+
color: 'lime'
229+
}, {
230+
// By default, weight is 0 when not set. So here, color:lime will be applied last,
231+
// overriding previous definitions blocks.
232+
weight: 10
233+
})
234+
235+
// If you have some issues and unexpected CSS applied, you may want debug things
236+
// so you can use `debug()` function that will log props, variants, CSS parts and
237+
// final merged CSS object.
238+
.debug()
239+
240+
// Finally, merge all CSS definitions and variants.
241+
// End function will return a CSS object.
242+
.end()
243+
})
244+
245+
// Create a component and render a "primary" button.
246+
function ButtonComponent() {
247+
return (
248+
<Div type="primary">
249+
<button>Button</button>
250+
</Div>
251+
)
252+
}
253+
254+
/* CSS will be:
255+
256+
{
257+
// defined in the first block
258+
'> button': {
259+
all: 'unset'
260+
},
261+
262+
// get from the primary background variant, it will override the background set in the second CSS block
263+
background: 'blue',
264+
265+
// get from the primary font variant, two variants applyed at the same time
266+
fontWeight: 'bold',
267+
fontStyle: 'italic'
268+
269+
// finally, the color will be lime, because the weight option overrides the color defined in the primary variant.
270+
color: 'lime'
271+
}
272+
273+
*/
274+
```
275+
276+
Have fun building variants! :)

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "build-variants",
3-
"version": "0.0.1-alpha.3",
3+
"version": "0.0.1-alpha.4",
44
"description": "Build and manage styles variants for any libraries.",
55
"author": "Alexis MINEAUD",
66
"license": "MIT",

0 commit comments

Comments
 (0)