UNPKG

@mincho-js/css

Version:
1,284 lines (1,039 loc) 26.8 kB
# @mincho-js/css The @mincho-js/css package provides framework-agnostic APIs for CSS-in-JS styling. <p align="center"> <img src="https://raw.githubusercontent.com/mincho-js/mincho/main/assets/logo.png" alt="logo" width="300" height="300"> <br/> <a href="https://x.com/mincho_js" target="_blank"> <img src="https://img.shields.io/twitter/follow/mincho_js?style=social" alt="Twitter Follow" /> </a> </p> :seedling: Easy adoption from Vanilla Extract. :syringe: Vanilla Extract with the power of a preprocessor Features inspired by [Sass](https://sass-lang.com/), [Less](https://lesscss.org/), [Stylus](https://stylus-lang.com/), etc. :cartwheeling: Syntax optimized for TypeScript Carefully designed as if it were native to TypeScript. :globe_with_meridians: Works with any front-end framework or even without one. :lock: Still type-safe styles for TypeScript. --- ## Install This package has [vanilla extract](https://vanilla-extract.style/) as a peer dependency, so we install them together. You'll also need to set up [bundler integration](https://vanilla-extract.style/documentation/getting-started#bundler-integration). ```shell npm install @mincho-js/css @vanilla-extract/css # or yarn add @mincho-js/css @vanilla-extract/css # or pnpm install @mincho-js/css @vanilla-extract/css ``` ## Usage Define styles in a file named `.css.ts`: ```typescript // styles.css.ts import { css } from "@mincho-js/css"; export const container = css({ padding: 10 }); ``` `css()` returns a class name, which you can import and use in your app: ```typescript // app.ts import { container } from "./styles.css.ts"; document.write(` <section class="${container}"> ... </section> `); ``` ## API ### css() The `css()` function takes a style object and generates a unique class name for the given styles. **Usage Example** ```typescript // button.css.ts import { css } from "@mincho-js/css"; export const buttonCss = css({ backgroundColor: "blue", color: "white", padding: { Block: 10, Inline: 20 }, _hover: { backgroundColor: "darkblue" } }); // button.tsx import { buttonCss } from "./button.css"; export function MyButton() { return <button className={buttonCss}></button>; } ``` ### cssVariant() The `cssVariant()` function is used to define multiple styles. It allows easy creation of components with multiple variants. **Usage Example** ```typescript // button.css.ts import { cssVariant } from '@mincho-js/css'; export const buttonVariants = cssVariant({ primary: { backgroundColor: "blue", color: "white" }, secondary: { backgroundColor: "gray", color: "black", "%primary &":{ color: "white" } }, danger: { backgroundColor: "red", color: { base: "white", "@media (prefers-color-scheme: dark)": "black" } } }); // button.tsx import { buttonVariants } from "./button.css"; interface ButtonProps { state: "primary" | "secondary" | "danger"; } export function MyButton({ state }: ButtonProps) { return <button className={buttonVariants[state]}></button>; } ``` ### rules() The `rules()` function is used to define both static and dynamic styles for reusable blocks. ```typescript // button.css.ts import { rules } from '@mincho-js/css'; export const button = rules({ padding: { Block: 10, Inline: 20 }, props: ["margin"], toggles: { rounded: { borderRadius: 999 } } variants: { color: { brand: { color: "#FFFFA0" }, accent: { color: "#FFE4B5" } }, size: { small: { padding: 12 }, medium: { padding: 16 }, large: { padding: 24 } } } compoundVariants: ({ color, size }) => [ { condition: [color.brand, size.small], style: { fontSize: "16px" } } ] }); // button.tsx import { button } from "./button.css"; export function MyButton() { return (<button className={button(["rounded", { color: "brand", size: "small" }])} style={button.props({ margin: "20px" })} ></button>); } ``` ## Features Some features are already implemented in Vanilla Extract, but we're assuming a first-time reader. Instead, we've attached an emoji to make it easier to distinguish. - Vanilla Extract: :cupcake: - Mincho: :icecream: ### 1. CSS Module :cupcake: We need to have a hash value to solve the problem of overlapping class names. [Vanilla Extract's `style()`](https://vanilla-extract.style/documentation/api/style/) is already doing a good job. **Code:** ```typescript const myCss = css({ color: "blue", backgroundColor: "#EEEEEE" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { color: blue; background-color: #EEEEEE; } ``` [Identifiers](https://vanilla-extract.style/documentation/integrations/vite/#identifiers) can be changed with settings. ### 2. Unitless Properties :cupcake: [Unitless Properties](https://vanilla-extract.style/documentation/styling#unitless-properties) is convenient because it reduces unnecessary string representations. **Code:** ```typescript export const myCss = css({ // cast to pixels padding: 10, marginTop: 25, // unitless properties flexGrow: 1, opacity: 0.5 }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { padding: 10px; margin-top: 25px; flex-grow: 1; opacity: 0.5; } ``` ### 3. Vendor Prefixes :cupcake: [Vendor Prefixes](https://vanilla-extract.style/documentation/styling#vendor-prefixes) is convenient because it reduces unnecessary string representations. **Code:** ```typescript export const myCss = css({ WebkitTapHighlightColor: "rgba(0, 0, 0, 0)" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } ``` ### 4. Fallback Styles :cupcake: [Fallback Styles](https://vanilla-extract.style/documentation/styling#fallback-styles) is convenient because it reduces unnecessary properties. **Code:** ```typescript export const myCss = css({ // In Firefox and IE the "overflow: overlay" will be // ignored and the "overflow: auto" will be applied overflow: ["auto", "overlay"] }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { overflow: auto; overflow: overlay; } ``` ### 5. Merge Values :icecream: Inspired by the [Less's Merge properties](https://lesscss.org/features/#merge-feature), this feature allows you to composition long split values. - If they end in `$`, they are joined by a comma - if they end in `_`, they are joined by a whitespace **Code:** ```typescript export const myCss = css({ boxShadow$: ["inset 0 0 10px #555", "0 0 20px black"], transform_: ["scale(2)", "rotate(15deg)"] }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { boxShadow: inset 0 0 10px #555, 0 0 20px black; transform: scale(2) rotate(15deg); } ``` For use with Fallback Styles, use a double array. It's automatically composited. **Code:** ```typescript export const myCss = css({ transform_: [ // Apply to all "scale(2)", // Fallback style ["rotate(28.64deg)", "rotate(0.5rad)"] ] }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { transform: scale(2) rotate(28.64deg); transform: scale(2) rotate(0.5rad); } ``` ### 6. Simply Important :icecream: Inspired by the [Tailwind's Important modifier](https://tailwindcss.com/docs/configuration#important-modifier), If `!` is at the end of the value, treat it as `!important`. **Code:** ```typescript export const myCss = css({ color: "red!" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { color: red !important; } ``` ### 7. CSS Variables :icecream: Unlike [Vanilla Extract's CSS Variables](https://vanilla-extract.style/documentation/styling#css-variables), it is supported at the top level. Inspired by the [SASS Variable](https://sass-lang.com/documentation/variables/), You can use `$` like you would a variable. The conversion to prefix and `kebab-case` happens automatically. **Code:** ```typescript export const myCss = css({ $myCssVariable: "purple", color: "$myCssVariable", backgroundColor: "$myOtherVariable(red)" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { --my-css-variable: purple; color: var(--my-css-variable); background-color: var(--my-other-variable, red); } ``` ### 8. Simple Pseudo Selectors :icecream: [Simple Pseudo Selectors](https://vanilla-extract.style/documentation/styling#simple-pseudo-selectors) is convenient because these are the elements you typically use with ["&"](https://sass-lang.com/documentation/style-rules/parent-selector/), so keep it. Inspired by the [Panda CSS's Conditional Styles](https://panda-css.com/), `_` is used as `:`. However, no other classes or attributes are added, it's a simple conversion. `camelCase` also convert to `kebab-case`. **Code:** ```typescript export const myCss = css({ _hover: { color: "pink" }, _firstOfType: { color: "blue" }, __before: { content: "" } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH]:hover { color: pink; } .[FILE_NAME]_myCSS__[HASH]:first-of-type { color: blue; } .[FILE_NAME]_myCSS__[HASH]::before { content: ""; } ``` ### 9. Simple Attribute Selectors :icecream: Allow toplevel [`attribute selector`](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) to prevent deep nesting. It would be nice to be able to autocomplete [HTML attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes). If the start is `[` without `&` treat it as `attribute selectors`. It is a continuation of Simple Pseudo Selectors. **Code:** ```typescript export const myCss = css({ "[disabled]": { color: "red" }, `[href^="https://"][href$=".org"]`: { color: "blue" } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH][disabled] { color: red; } .[FILE_NAME]_myCSS__[HASH][href^="https://"][href$=".org"] { color: blue; } ``` ### 10. Complex Selectors :cupcake: / :icecream: Unlike [Vanilla Extract's Complex Selectors](https://vanilla-extract.style/documentation/styling#complex-selectors), it is supported at the top level. I want to reduce nesting as much as possible. Exception values for all properties are treated as complex selectors. **Code:** ```typescript export const myCss = css({ "&:hover:not(:active)": { border: "2px solid aquamarine" }, "nav li > &": { textDecoration: "underline" } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH]:hover:not(:active) { border: 2px solid aquamarine; } nav li > .[FILE_NAME]_myCSS__[HASH] { text-decoration: underline; } ``` > [!WARNING] > Constraints like circular reference still apply. #### Complex Selectors - Reference constraints That it inherits all of Vanilla Extract's constraints. ```typescript const invalid = css({ // ERROR: Targetting `a[href]` "& a[href]": {...}, // ERROR: Targetting `.otherClass` "& ~ div > .otherClass": {...} }); // Also Invalid example: export const child = css({}); export const parent = css({ // ERROR: Targetting `child` from `parent` [`& ${child}`]: {...} }); // Valid example: export const parent = css({}); export const child = css({ [`${parent} &`]: {...} }); ``` #### Complex Selectors - Circular reference As above, [Circular reference](https://vanilla-extract.style/documentation/styling/#circular-selectors) is the same. ```typescript export const child = css({ background: "blue", get selectors() { return { [`${parent} &`]: { color: 'red' } }; } }); export const parent = css({ background: "yellow", selectors: { [`&:has(${child})`]: { padding: 10 } } }); ``` ### 11. At-Rules :cupcake: / :icecream: Allows nesting, like [Vanilla Extract's Media Queries](https://vanilla-extract.style/documentation/styling#media-queries), and also allows top-levels. **Code:** ```typescript export const myCss = css({ // Nested "@media": { "screen and (min-width: 768px)": { padding: 10 }, "(prefers-reduced-motion)": { transitionProperty: "color" } }, // Top level "@supports (display: grid)": { display: "grid" } }); ``` **Compiled:** ```css @media screen and (min-width: 768px) { .[FILE_NAME]_myCSS__[HASH] { padding: 10px; } } @media (prefers-reduced-motion) { .[FILE_NAME]_myCSS__[HASH] { transition-property: color; } } @supports (display: grid) { .[FILE_NAME]_myCSS__[HASH] { display: grid; } } ``` ### 12. Anonymous At-Rules :icecream: Inspired by the [Griffel's Keyframes](https://griffel.js.org/react/api/make-styles#keyframes-animations), Makes [`@keyframes`](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes) or [`@font-face`](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) writable inline. `fontFamily$` is used as special case of the `Merge Values` rule. **Code:** ```typescript export const myCss = css({ // Keyframes animationName: { "0%": { transform: "rotate(0deg)" }, "100%": { transform: "rotate(360deg)" } }, animationDuration: "3s", // Fontface fontFamily: { src: "local('Comic Sans MS')" }, // Fontface with multiple fontfamily$: [{ src: "local('Noto Sans')" }, { src: "local('Gentium')" }] }); ``` **Compiled:** ```css @keyframes [FILE_NAME]_myCSSKeyframes__[HASH] { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @font-face { src: local("Comic Sans MS"); font-family: "[FILE_NAME]_myCSSFontFace1__[HASH]"; } @font-face { src: local("Noto Sans"); font-family: "[FILE_NAME]_myCSSFontFace2__[HASH]"; } @font-face { src: local("Gentium"); font-family: "[FILE_NAME]_myCSSFontFace3__[HASH]"; } .[FILE_NAME]_myCSS__[HASH] { animation-name: [FILE_NAME]_myCSSKeyframes__[HASH]; animation-duration: 3s; font-family: [FILE_NAME]_myCSSFontFace1__[HASH]; font-family: [FILE_NAME]_myCSSFontFace2__[HASH], [FILE_NAME]_myCSSFontFace3__[HASH]; } ``` ### 13. Nested Properties :icecream: Inspired by the [SCSS's nested properties](https://sass-lang.com/documentation/style-rules/declarations/#nesting), this feature allows nesting for property names. Reduce redundancy and make your context stand out. Uppercase it to distinguish it from `Property based condition`. [`Vendor Prefixes`](./000-css-literals.md#5-vendor-prefixes) exists only in Top level, while `Nested Properties` exists only in nesting, so you can tell them apart. **Code:** ```typescript export const myCss = css({ transition: { Property: "font-size", Duration: "4s", Delay: "2s" } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { transition-property: font-size; transition-duration: 4s; transition-delay: 2s; } ``` ### 14. Property based condition :icecream: Inspired by the [Panda CSS](https://panda-css.com/docs/concepts/conditional-styles#property-based-condition), You can apply properties based on selectors or at-rules. The default properties refer to `base`. ```typescript export const myCss = css({ color: { base: "red", _hover: "green", "[disabled]": "blue", "nav li > &": "black", "@media (prefers-color-scheme: dark)": "white" } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { color: red; } .[FILE_NAME]_myCSS__[HASH]:hover { color: green; } .[FILE_NAME]_myCSS__[HASH][disabled] { color: blue; } nav li > .[FILE_NAME]_myCSS__[HASH] { color: black; } @media (prefers-color-scheme: dark) { .[FILE_NAME]_myCSS__[HASH] { color: red; } } ``` ### 15. Nested Selectors :icecream: Inspired by the [SCSS's nested selectors](https://sass-lang.com/documentation/style-rules/parent-selector/), this feature allows nesting for selectors. It works with [Simple Pseudo Selectors](./000-css-literals.md#10-simple-pseudo-selectors) and [Complex Selectors](./000-css-literals.md#11-complex-selectors). ```typescript export const myCss = css({ "nav li > &": { color: "red", _hover: { color: "green" }, "&:hover:not(:active)": { color: "blue" }, ":root[dir=rtl] &": { color: "black" } } }); ``` **Compiled:** ```css nav li > .[FILE_NAME]_myCSS__[HASH] { color: red; } nav li > .[FILE_NAME]_myCSS__[HASH]:hover { color: green; } nav li > .[FILE_NAME]_myCSS__[HASH][disabled]:hover:not(:active) { color: blue; } :root[dir=rtl] nav li > .[FILE_NAME]_myCSS__[HASH] { color: black; } ``` ### 16. Nested At-Rules :icecream: Like `Nested Selectors`, but they are hoisted and combined into a `AND` rule. Depending on the `Ar-Rules` keyword, the combining syntax is slightly different. (Unlike [`@media`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media), [`@supports`](https://developer.mozilla.org/en-US/docs/Web/CSS/@supports), and [`@container`](https://developer.mozilla.org/en-US/docs/Web/CSS/@container), [`@layer`](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) is displayed like `parent.child`.) **Code:** ```typescript export const myCss = css({ "nav li > &": { color: "red", "@media (prefers-color-scheme: dark)": { "@media": { "(prefers-reduced-motion)": { color: "green" }, "(min-width: 900px)": { color: "blue" } } }, "@layer framework": { "@layer": { "layout": { color: "black" }, "utilities": { color: "white" } } } } }); ``` **Compiled:** ```css nav li > .[FILE_NAME]_myCSS__[HASH] { color: red; } @media (prefers-color-scheme: dark) and (prefers-reduced-motion) { nav li > .[FILE_NAME]_myCSS__[HASH] { color: green; } } @media (prefers-color-scheme: dark) and (min-width: 900px) { nav li > .[FILE_NAME]_myCSS__[HASH] { color: blue; } } @layer framework.layout { nav li > .[FILE_NAME]_myCSS__[HASH] { color: blue; } } @layer framework.utilities { nav li > .[FILE_NAME]_myCSS__[HASH] { color: blue; } } ``` It can be used with `Property based condition`. **Code:** ```typescript export const myCss = css({ "nav li > &": { color: { base: "red", "@media (prefers-color-scheme: dark)": { "@media (prefers-reduced-motion)": "green", "@media (min-width: 900px)": "blue" }, "@layer framework": { "@layer": { "layout": "black", "utilities": "white" } } } } }); ``` ### 17. Property Reference :icecream: Inspired by the [Stylus's property lookup](https://stylus-lang.com/docs/variables.html#property-lookup), this feature can be used to refer to a property value. **Code:** ```typescript export const myCss = css({ width: "50px", height: "@width", margin: "calc(@width / 2)" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { width: 50px height: 50px; margin: calc(50px / 2); } ``` When used alone, like `"@flexGrow"`, you can use the literal value it refers to. **Code:** ```typescript export const myCss = css({ flexGrow: 1, flexShrink: "@flexGrow" }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { flex-grow: 1; flex-shrink: 1; } ``` ### 18. Variants Reference :icecream: Inspired by the [JSS plugin nested](https://cssinjs.org/jss-plugin-nested?v=v10.10.0#use-rulename-to-reference-a-local-rule-within-the-same-style-sheet), this feature can be reference a local rule. Use the `%` symbol. **Code:** ```typescript export const myCss = cssVariant({ primary: { color: "red", ":has(%secondary)": { color: "blue", } }, secondary: { color: "black", "%primary &":{ color: "white" } } }); ``` **Compiled:** ```css .[FILE_NAME]_myCSS_primary__[HASH] { color: red; } .[FILE_NAME]_myCSS_primary__[HASH]:has(.[FILE_NAME]_myCSS_secondary__[HASH]) { color: blue; } .[FILE_NAME]_myCSS_secondary__[HASH] { color: black; } .[FILE_NAME]_myCSS_primary__[HASH] .[FILE_NAME]_myCSS_secondary__[HASH] { color: white; } ``` ### 19. CSS Composition :cupcake: [Vanilla Extract's composition](https://vanilla-extract.style/documentation/style-composition/) is well enough made, so keep it. **Code:** ```typescript const base = css({ padding: 12 }); const primary = css([base, { background: "blue" }]); const secondary = css([base, { background: "aqua" }]); ``` **Compiled:** ```css .[FILE_NAME]_base__[HASH] { padding: 12px; } .[FILE_NAME]_base__[HASH] { background: blue; } .[FILE_NAME]_base__[HASH] { background: aqua; } ``` ### 20. CSS Rules :cupcake: Define it as an object style, similar to css. **Code:** ```typescript const myRule = rules({ color: "blue", backgroundColor: "red" }); ``` **Compiled:** ```css .[FILE_NAME]_myRule__[HASH] { color: blue; background-color: red; } ``` However, it is returned as a function, so you need to run it to use it. ```typescript function MyComponent() { return <div className={myCSS()}></div>; } ``` ### 21. Rules Props :icecream: Provides dynamic styles using CSS Variables. **Code:** ```typescript const myRule = rules({ props: ["color", "background", { size: { targets: ["padding", "margin"] }}] }); ``` **Compiled:** ```css .[FILE_NAME]_myRule__[HASH] { color: var(--[FILE_NAME]_myRule_color__[HASH]); background: var(--[FILE_NAME]_myRule_background__[HASH]); padding: var(--[FILE_NAME]_myRule_size__[HASH]); margin: var(--[FILE_NAME]_myRule_size__[HASH]); } ``` You can also set a default value. **Code:** ```typescript const myRule = rules({ props: [ "color", { background: { base: "red", targets: ["background"] }, size: { base: "3px", targets: ["padding", "margin"] } } ] }); ``` **Compiled:** ```css .[FILE_NAME]_myRule__[HASH] { color: var(--[FILE_NAME]_myRule_color__[HASH]); background: var(--[FILE_NAME]_myRule_background__[HASH], red); padding: var(--[FILE_NAME]_myRule_size__[HASH], 3px); margin: var(--[FILE_NAME]_myRule_size__[HASH], 3px); } ``` You can think of use cases as those that are statically extracted and those that are dynamically assigned. **Static Usage:** ```typescript const myCSS = css([ myRule.props({ color: "red", background: "blue", size: "5px" }), { borderRadius: 999 } ]); ``` **Compiled:** ```css .[FILE_NAME]_myCSS__[HASH] { --myCSS_color__[HASH]: red; --myCSS_background__[HASH]: blue; --myCSS_size__[HASH]: 5px; border-radius: 999px; } ``` If dynamic case, it is assigned as an inline style. **Dynamic Usage** ```typescript import { myRule } from "sample.css"; function Sample({ color }) { return <div style={myRule.props({ color })}>contents...</div>; } ``` ### 22. Rules Variants :cupcake: [Stitches's `variants`](https://stitches.dev/docs/variants#adding-variants) is well enough made. **Code:** ```typescript const button = rules({ color: "black", backgroundColor: "white", borderRadius: 6, variants: { color: { brand: { color: "#FFFFA0", backgroundColor: "blueviolet" }, accent: { color: "#FFE4B5", backgroundColor: "slateblue" } }, size: { small: { padding: 12 }, medium: { padding: 16 }, large: { padding: 24 } }, rounded: { true: { borderRadius: 999 } } } }); ``` **Compiled:** ```css .[FILE_NAME]_button__[HASH] { color: black; background-color: white; border-radius: 6px; } .[FILE_NAME]_button_color_brand__[HASH] { color: #ffffa0; background-color: blueviolet; } .[FILE_NAME]_button_color_accent__[HASH] { color: #ffe4b5; background-color: slateblue; } .[FILE_NAME]_button_size_small__[HASH] { padding: 12px; } .[FILE_NAME]_button_size_medium__[HASH] { padding: 16px; } .[FILE_NAME]_button_size_large__[HASH] { padding: 24px; } ``` You can use it as if you were using `css`. **Usage:** ```typescript button({ color: "accent", size: "large", rounded: true }); ``` ### 23. Toggle Variants :cupcake: / :icecream: [Stitches's `boolean variants`](https://stitches.dev/docs/variants#boolean-variants) are a special case, but the syntax for defining them is awkward. Therefore, we introduce a specialized syntax. **Code Before:** ```typescript const button = rules({ // base styles variants: { // common variants rounded: { true: { borderRadius: 999 } } } }); ``` **Code After:** ```typescript const button = rules({ // base styles toggles: { rounded: { borderRadius: 999 } } variants: { // common variants } }); ``` ### 24. Compound Variants :icecream: [Stitches's `Compound Variants`](https://stitches.dev/docs/variants#compound-variants) is an effective way to set up additional css by leveraging the combination of variations you have already set up. However, the method of writing the conditions seems quite inconvenient when conditions are complicated. So we want to improve the UX in this area. **Code Before:** ```typescript const button = rules({ // base styles variants: { color: { brand: { color: "#FFFFA0" }, accent: { color: "#FFE4B5" } }, size: { small: { padding: 12 }, medium: { padding: 16 }, large: { padding: 24 } } }, compoundVariants: [ { variants: { color: "brand", size: "small", }, style: { fontSize: "16px" } } ] }); ``` It doesn't seem uncomfortable when the conditions are not as demanding as they are now. But if the conditions become complicated, it will be inconvenient to fill out. **Code After:** ```typescript const button = rules({ // base styles variants: { color: { brand: { color: "#FFFFA0" }, accent: { color: "#FFE4B5" } }, size: { small: { padding: 12 }, medium: { padding: 16 }, large: { padding: 24 } } }, compoundVariants: ({ color, size }) => [ { condition: [color.brand, size.small], style: { fontSize: "16px" } } ] }); ``` **Compiled:** ```css .[FILE_NAME]_button_compound_0__[HASH] { font-size: 16px; } .[FILE_NAME]_button_compound_1__[HASH] { font-size: 24px; font-weight: bold; } ``` ### 25. Default Variants :cupcake: The way of [Stitches's `Default Variants`](https://stitches.dev/docs/variants#default-variants) is already good to use, so we keep this method in ours. **Code:** ```typescript const button = rules({ // base styles variants: { color: { brand: { color: "#FFFFA0", backgroundColor: "blueviolet" }, accent: { color: "#FFE4B5", backgroundColor: "slateblue" } } }, defaultVariants: { color: "brand" } }); ``` ## Contributing We welcome contributions! Please see our [Contributing Guide](/CONTRIBUTING.md) for more details. ## License This project is licensed under the [MIT License](/LICENSE).