Skip to content

Instantly share code, notes, and snippets.

@mattcompiles
Created March 26, 2021 10:13
Show Gist options
  • Save mattcompiles/2e7c77fb17746034c3f732308b91f8a1 to your computer and use it in GitHub Desktop.
Save mattcompiles/2e7c77fb17746034c3f732308b91f8a1 to your computer and use it in GitHub Desktop.
vanilla-extract - Stitches style API demo
import { stitch } from './vanilla-stitches-runtime';
import { stackPattern, buttonPattern } from './styles.css';
const button = stitch(buttonPattern);
const stack = stitch(stackPattern);
document.body.innerHTML = `
<div class="${stack({ space: 'xlarge' })}">
<div class="${stack({ space: 'small' })}">
<button class="${button()}">Button</button>
<button class="${button({ variant: 'purple' })}">Purple button</button>
</div>
<div class="${stack()}">
<button class="${button({ size: 'large' })}">Large Button</button>
<button class="${button({
variant: 'purple',
size: 'large',
})}">Large Purple button</button>
</div>
<div>
`;
import mapValues from 'lodash/mapValues';
import { createGlobalTheme } from '@vanilla-extract/css';
import { createPattern } from './vanilla-stitches-style';
const gray800 = '#888';
const vars = createGlobalTheme(':root', {
color: {
gray400: 'gainsboro',
gray500: 'lightgray',
gray800: gray800,
purple400: 'blueviolet',
purple500: 'darkviolet',
},
space: {
xsmall: '4px',
small: '8px',
medium: '20px',
large: '28px',
xlarge: '40px',
},
shadows: {
small: [
`0 2px 4px 0px ${gray800}`,
`0 2px 2px -2px ${gray800}`,
`0 4px 4px -4px ${gray800}`,
].join(', '),
medium: [
`0 2px 4px 0px ${gray800}`,
`0 8px 8px -4px ${gray800}`,
`0 12px 12px -8px ${gray800}`,
].join(', '),
large: [
`0 2px 4px 0px ${gray800}`,
`0 12px 12px -4px ${gray800}`,
`0 20px 20px -12px ${gray800}`,
].join(', '),
},
});
export const buttonPattern = createPattern({
appearance: 'none',
border: 'none',
borderRadius: 99999,
lineHeight: 1,
variants: {
variant: {
gray: {
backgroundColor: vars.color.gray400,
':hover': {
backgroundColor: vars.color.gray500,
},
},
purple: {
backgroundColor: vars.color.purple400,
color: 'white',
':hover': {
backgroundColor: vars.color.purple500,
},
},
},
size: {
standard: {
fontSize: 13,
height: 25,
paddingLeft: vars.space.small,
paddingRight: vars.space.small,
},
large: {
fontSize: 16,
height: vars.space.xlarge,
paddingLeft: vars.space.medium,
paddingRight: vars.space.medium,
},
},
},
compoundVariants: [
{
size: 'large',
variant: 'purple',
css: {
boxShadow: vars.shadows.large,
},
},
],
defaultVariants: {
variant: 'gray',
size: 'standard',
},
});
export const stackPattern = createPattern({
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
variants: {
space: mapValues(vars.space, (spaceValue) => ({
gap: spaceValue,
})),
},
defaultVariants: {
space: 'medium',
},
});
import mapValues from 'lodash/mapValues';
import { style, mapToStyles } from '@vanilla-extract/css';
import { StitchesOptions, StitchesResult, VariantGroups } from './vanilla-stitches-types';
export function createPattern<Variants extends VariantGroups>(
options: StitchesOptions<Variants>,
): StitchesResult<Variants> {
const { variants, defaultVariants, compoundVariants = [], ...rest } = options;
const defaultClassName = style(rest);
const variantClassNames = mapValues(variants, (variantGroup) =>
mapToStyles(variantGroup),
) as StitchesResult<Variants>['variantClassNames'];
const compoundVariantsValues = compoundVariants.map((cp) => {
const { css, ...variantOptions } = cp;
const className = style(css);
return {
className,
variantOptions,
};
});
return {
defaultClassName,
variantClassNames,
defaultVariants,
compoundVariantsValues,
};
}
import mapValues from 'lodash/mapValues';
import { style, mapToStyles } from '@vanilla-extract/css';
import { StitchesOptions, StitchesResult, VariantGroups } from './vanilla-stitches-types';
export function createPattern<Variants extends VariantGroups>(
options: StitchesOptions<Variants>,
): StitchesResult<Variants> {
const { variants, defaultVariants, compoundVariants = [], ...rest } = options;
const defaultClassName = style(rest);
const variantClassNames = mapValues(variants, (variantGroup) =>
mapToStyles(variantGroup),
) as StitchesResult<Variants>['variantClassNames'];
const compoundVariantsValues = compoundVariants.map((cp) => {
const { css, ...variantOptions } = cp;
const className = style(css);
return {
className,
variantOptions,
};
});
return {
defaultClassName,
variantClassNames,
defaultVariants,
compoundVariantsValues,
};
}
import { StyleRule } from '@vanilla-extract/css';
export type EnumVariant = Record<string | number, StyleRule>;
export type VariantTypes = EnumVariant;
export type VariantGroups = Record<string, VariantTypes>;
export type VariantSelection<Variants extends VariantGroups> = {
[variantGroup in keyof Variants]?: keyof Variants[variantGroup];
};
export interface StitchesResult<Variants extends VariantGroups> {
defaultClassName: string;
variantClassNames: {
[P in keyof Variants]: { [P in keyof Variants[keyof Variants]]: string };
};
defaultVariants?: VariantSelection<Variants>;
compoundVariantsValues: Array<{
className: string;
variantOptions: Omit<CompoundVariant<Variants>, 'css'>;
}>;
}
export type CompoundVariant<
Variants extends VariantGroups
> = VariantSelection<Variants> & {
css: StyleRule;
};
export type StitchesOptions<Variants extends VariantGroups> = StyleRule & {
variants?: Variants;
defaultVariants?: VariantSelection<Variants>;
compoundVariants?: Array<CompoundVariant<Variants>>;
};
@CraigCav
Copy link

CraigCav commented Jun 23, 2021

From twitter:

it appears the vanilla-stitches-runtime.ts file might not have the right content? [...]The content looks the same as the vanilla-stitches-style.ts file, so perhaps that was accidentally committed in it's place?

I've thrown together a codesandbox here to see if I could get this up and running.

As the vanilla-stitches-runtime.ts file above didn't look right, I quickly threw together the following code, which seems to work, but is missing some type definitions for the props value:

import { StitchesResult, VariantGroups } from "./vanilla-stitches-types";

export const stitch = <Variants extends VariantGroups>(
  pattern: StitchesResult<Variants>
) => (props?: any): string => {
  const classNames = [pattern.defaultClassName];

  const propsWithDefaults = {
    ...(pattern.defaultVariants || {}),
    ...(props || {})
  };

  for (const [name, value] of Object.entries(pattern.variantClassNames)) {
    if (name in propsWithDefaults) {
      const propValue = propsWithDefaults[name];
      classNames.push(value[propValue] || "");
    }
  }

  for (const { className, variantOptions } of pattern.compoundVariantsValues) {
    if (
      props &&
      Object.entries(variantOptions).every(
        ([name, value]) => propsWithDefaults[name] === value
      )
    ) {
      classNames.push(className);
    }
  }

  return classNames.join(" ");
};

I would definitely be interested in seeing if anyone else has attempted to do something similar to this. The variants API in stitches is 🔥 so combining that with vanilla-extract would be really neat.

I wonder if it's possible to support something similar to stitches responsive-variants with vanilla-extract? It basically lets you say "for small screens use this variant and for large screens use this other variant". I guess with the combinatorial effect of props on media queries, it might lend itself to being paired with sprinkles?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment