Closed alexandernanberg closed 2 years ago
Yeah, this would be nice. Maybe
// Box.styles.ts
import { styleVariants } from '@vanilla-extract/css';
variants = styleVariants(...);
// Box.ts
export const Box = createBox({
atoms,
variants
});
Similarly, I'd like to add extra styles. For example I could make a "Box" called Button that always includes some base styles. This way dessert-box would become similar to styled components but for Vanilla-extract/Sprinkles. Basically we'd extend the concept from "A single Box of all atom props" to "Boxes of composed atoms, variants, and styles".
Example code: https://github.com/graup/nf-new/blob/4fd97b7ef418787409704e95bd2522fbf84bdb04/src/foundation/Button/Button.tsx
This code seems like it could be replaced by createBox({ atoms, variants, classNames })
@alexandernanberg @graup awesome, got it! Thank you so much for the feedback and sorry for the delay here. I'll hack something together in the weekend and notify here with some proposals or once it's released, but I like the idea of the proposed API @graup, thanks!
Oops didn't mean to close.
@graup I have something working with this API:
tsx
const colorStyles = createAtomicStyles({
conditions: {
lightMode: { '@media': '(prefers-color-scheme: light)' },
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: false,
properties: {
backgroundColor: { green: 'green', red: 'red' }
}
});
const space = {
none: '0',
small: '4px',
medium: '8px',
large: '16px',
extraLarge: '32px'
};
const layoutStyles = createAtomicStyles({
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' }
},
defaultCondition: 'mobile',
properties: {
padding: space,
},
shorthands: {
}
});
const atoms = createAtomsFn(layoutStyles);
const Button = createBox({
atoms,
variants: {
type: {
success: { backgroundColor: 'green' },
warning: { backgroundColor: 'red' },
},
size: {
small: {
padding: { mobile: 'small', desktop: 'medium' }
},
large: {
padding: { mobile: 'medium', desktop: 'large' }
}
}
}
})
<Button type="success" size="small" />
The idea is that variants
can be defined as a group of box props with a certain values, and you'll be able to pass multiple variants (e.g. type
and size
in the example above). This will be completely typed, so autocompletion will be there.
What do you think of this API?
@TheMightyPenguin I like this, seems to match Sprinkle's design better than what I suggested with styleVariants
.
This looks great!
@TheMightyPenguin I'm up for testing this if you create a release.
Awesome, @graup @tmm thanks for the feedback, expect a new release over the upcoming weekend 🙌
@TheMightyPenguin any luck? I'm so excited for this.
Hi all, sorry for the long delay, but here's the long awaited Variant API!
It's quite different from what we discussed here in this issue, but it goes more aligned with the vision for this library. I spend some time thinking what this library meant and what's the vision I had initially.
This library is meant to be used to create a Box component, and then to use that Box component as a low-level utility to consume design tokens, meaning it could be used once to create that Box, and then use that Box component to compose and build other components across your project. With this in mind, this is the API I ended up with:
createVariants
:
// Box.tsx
import { createBox } from 'dessert-box';
import { atoms } from './sprinkles.css';
// notice we export the createVariants function const { Box, createVariants } = createBox({ atoms });
export default Box
2. Use your Box together with `createVariants` to implement new components, like a Text one:
// Text.tsx import { Box, createVariants } from "./Box";
const variants = createVariants({ h1: { fontSize: "extraLarge", fontWeight: "600", }, h2: { // any design token from your sprinkles atoms can be used here fontSize: "large", fontWeight: "400", }, p: { fontSize: { // Including conditional properties. All of this autocompletes with TS desktop: "medium", mobile: "large", }, }, });
type Props = { variant: keyof typeof variants; children: React.ReactNode; };
const Text = ({ variant, children }: Props) => { return <Box {...variants[variant]}>{children}; };
export default Text;
You can check the docs here: https://github.com/TheMightyPenguin/dessert-box#variants
Please let me know how it works for you and any feedback, I hope this also works for your use cases! If there's some friction moving to this API or any questions let me know.
CC @tmm @graup @alexandernanberg
@TheMightyPenguin looks great!
Might be worth noting in the docs that you can apply multiple variants since it's not immediately obvious. Something like this:
// Button.tsx
import { Box, createVariants } from "./Box"
const variants = createVariants({
primary: {
background: 'blue',
},
secondary: {
background: 'gray',
},
})
const sizes = createVariants({
md: {
fontSize: 'large',
},
lg: {
fontSize: 'extraLarge',
},
})
type Props = {
children: React.ReactNode;
size?: keyof typeof sizes
variant?: keyof typeof variants
// More props...
}
export const Button = ({
children,
size = 'md',
variant = 'secondary',
// More props...
}: Props) => {
return (
<Box as="button" {...sizes[size]} {...variants[variant]}>
{children}
</Box>
)
}
When trying it out, the as
prop on Box
was creating a type error (see below) when I spread any of the createVariants
values (i.e. {...variants[variant]}
):
Type '{ children: ReactNode; background?: ConditionalStyle<Values<MapLeafNodes<{ black: string; blue: string; current: string; transparent: string; white: string; }, CSSVarFunction>, { ...; }>>; ... 50 more ...; as: "button"; }' is not assignable to type 'Omit<AllHTMLAttributes<HTMLElement>, "as" | "width" | "height">'.
Types of property 'color' are incompatible.
Type 'ConditionalStyle<Values<MapLeafNodes<{ black: string; blue: string; current: string; transparent: string; white: string; }, CSSVarFunction>, { defaultClass: string; conditions: { ...; }; }>>' is not assignable to type 'string'.
Type '{ sm?: "transparent" | "black" | "blue" | "current" | "white"; md?: "transparent" | "black" | "blue" | "current" | "white"; lg?: "transparent" | "black" | "blue" | "current" | "white"; xl?: "transparent" | ... 3 more ... | "white"; }' is not assignable to type 'string'.ts(2322)
@tmm great idea, added that into the example in the docs!
Thanks for pointing this out, working on fixing this type error.
@TheMightyPenguin fix for this: https://github.com/TheMightyPenguin/dessert-box/issues/3#issuecomment-907887884
Filtered out the color
prop from React.AllHTMLAttributes
@tmm released 0.3.3, thank you so much for the contribution! (Btw, are you using this at https://mirror.xyz? fan of that product!)
@TheMightyPenguin np!
Maybe at some point. Looking into vanilla-extract
more generally, but lack of simple responsive variants is sort of a blocker.
@tmm what variant API would help you to use this? I'm up for a meeting sometime as well btw, I love working on Design Systems (working at one at work at the moment and it uses dessert-box to build our foundational Box component), so I can share some lessons learned and we can talk about your use case!
@tmm @graup @alexandernanberg so as @vanilla-extract/recipes
was released, that does provide a great API for variants when using vanilla extract. So with that in mind the createVariants
API was removed, and instead I documented how to use this library with recipes:
https://github.com/TheMightyPenguin/dessert-box#variants
I'll be documenting more patterns, and common use cases in the README!
Closing as @vanilla-extract/recipes
solves this use case!
From what I can tell
dessert-box
doesn't support variant styles yet, and I think it would be a great addition.The API I'm looking for is something like
The variant prop would need to be able to set multiple style properties, e.g. setting
variant=primary
could changefontSize
,backgroundColor
etc