Chronstruct / primitives

MIT License
3 stars 0 forks source link

variants syntax #55

Open kylpo opened 4 years ago

kylpo commented 4 years ago

2 kinds of dynamic:

Helpful links/ideas:

Here are some of my attempts for syntax:

export const Text = ({
  as: Comp = "span",
  color = "dark",
  ...props
}: Props) => (
  <txt
    tag={Comp}
    // variant={props.style}
    // variant={{ style: "display", color: "red" }}
    font={{
      _variant: props.style,
      _variant_logo: LOGO_FONT,
      _variant_text: TEXT_FONT,
    }}
    size={{
      _variant: props.style,
      _variant_logo: {
        _: 30,
        [MEDIUM_UP]: 40,
      },
      _variant_text: 32,
    }}
    color={{
      _varyOn: props.color,
      _variant_grey: GREY,
      _variant_current: "currentColor",
    }}
    spacing={{
      $switch: props.color,
      _grey: GREY,
      _current: "currentColor",
    }}
    spacing={{
      $: props.color,
      $grey: GREY,
      $current: "currentColor",
    }}
    data-text-style={IS_DEV ? props.style : undefined}
    html={props.html}
  >
    props.children
  </txt>
)

A $ version:


export const Text = ({
  as: Comp = "span",
  color = "dark",
  ...props
}: Props) => (
  <txt
    tag={Comp}
    font={{
      $: props.style,
      logo: LOGO_FONT,
      text: TEXT_FONT,
    }}
    size={{
      $: props.style,
      logo: {
        _: 30,
        [MEDIUM_UP]: 40,
      },
      text: 32,
    }}
    spacing={{
      $: props.style,
      logo: {
        _: 30,
        [MEDIUM_UP]: 40,
      },
      text: 32,
    }}
    color={{
      $: props.color,
      grey: GREY,
      current: "currentColor",
    }}
    bold={props.bold}
    center={props.center}
    transform={props.allCaps && "uppercase"}
    html={props.html}
    className={props.className}
    // data-text-style={IS_DEV ? props.style : undefined}
  >
    props.children
  </txt>
)

an array solution:

export const Text = ({
  as: Comp = "span",
  color = "dark",
  ...props
}: Props) => (
  <txt
    tag={Comp}
    font={[
      props.style,
      {
        logo: LOGO_FONT,
        text: TEXT_FONT,
      },
    ]}
    size={[
      props.style,
      {
        logo: [
          30,
          {
            [MEDIUM_UP]: 40,
          },
        ],
        text: 32,
      },
    ]}
    spacing={[
      props.style,
      {
        logo: [
          30,
          {
            [MEDIUM_UP]: 40,
          },
        ],
        text: 32,
      },
    ]}
    color={[
      props.color,{
      grey: GREY,
      current: "currentColor",
    }]}
    bold={props.bold}
    center={props.center}
    transform={props.allCaps && "uppercase"}
    html={props.html}
    className={props.className}
    // data-text-style={IS_DEV ? props.style : undefined}
  >
    props.children
  </txt>
)
kylpo commented 4 years ago

And a more complicated Link component:

<box
      tag={Link}
      tag-to={props.to}
      grow={props.grow}
      padding={{
        $: props.kind,
        _: 20, // default case when props.kind isn't 'filled' or 'transparent'
        filled: "20px 40px",
        transparent: 16,
      }}
      style={{
        borderRadius: { varyOn: props.kind, filled: 50 },
        backgroundColor: {
          varyOn: `${props.kind}_${props.color}`,
          filled_white: WHITE,
        },
        boxShadow: {
          varyOn: (props) => props.kind === "outlined" && props.color,
          white: {
            _: `0 0 0 4px ${WHITE}`,
            ":hover": `0 0 0 6px ${WHITE}`,
          },
          grey: {
            _: `0 0 0 4px ${GREY}`,
            ":hover": `0 0 0 6px ${GREY}`,
          },
        },
      }}
      className={props.className}
    >
      <txt
        bold
        center
        as="div"
        style="t4"
        color={WHITE}
        transform={props.kind === "outlined" && "uppercase"}
      >
        {props.children}
      </txt>
    </box>
kylpo commented 4 years ago

Think of the varyOn/$/_variant as a switch statement, and the keys below are cases.

kylpo commented 4 years ago

Could use template literals for a new syntax?

padding={`
  ${props.kind}: {
    filled: "20px 40px",
    transparent: 16,
   }
`}

And function(ish) form

padding={`
  ${props.kind === "outlined" && props.color}: {
    filled: "20px 40px",
    transparent: 16,
   }
`}
kylpo commented 4 years ago

Here's the normal Linaria Text to compare against:

import React from "react"
import { css, cx } from "linaria"
import { MEDIUM, LARGE_UP } from "../util/breakpoints"
import {
  BlACK,
  WHITE,
  SILVER,
  GREY,
  PRIMARY,
  SECONDARY,
  SECONDARY_ACCENT,
  DARK,
} from "../util/colors"
import { IS_DEV } from "../util/environment"

/* Prop -> property maps */

const TEXT_FONT = "Arial, Helvetica, sans-serif"
// const HEADING_FONT = "Arial Black, sans-serif"
const HEADING_FONT = "Arial, Helvetica, sans-serif"

const BOLD_CLASS = css`
  font-weight: bold;
`
const CENTER_CLASS = css`
  text-align: center;
`
const ALL_CAPS_CLASS = css`
  text-transform: uppercase;
`

const styles = {
  t: css`
    font-family: ${TEXT_FONT};
  `,
  t1: css`
    font-family: ${TEXT_FONT};
    font-size: 20px;
    line-height: 24px;

    @media ${MEDIUM} {
      font-size: 24px;
      line-height: 32px;
    }

    @media ${LARGE_UP} {
      font-size: 26px;
      line-height: 34px;
    }
  `,
  t2: css`
    font-family: ${TEXT_FONT};
    font-size: 18px;
    line-height: 28px;

    @media ${MEDIUM} {
      font-size: 20px;
      line-height: 24px;
    }

    @media ${LARGE_UP} {
      font-size: 24px;
      line-height: 32px;
    }
  `,
  t3: css`
    font-family: ${TEXT_FONT};
    font-size: 16px;
    /* line-height: 18px; */
    line-height: 20px;
    letter-spacing: 0.3px;

    @media ${MEDIUM} {
      font-size: 18px;
      line-height: 28px;
    }

    @media ${LARGE_UP} {
      font-size: 20px;
      /* line-height: 28px; */
      line-height: 30px;
    }
  `,
  t4: css`
    font-family: ${TEXT_FONT};
    font-size: 16px;
    line-height: 24px;

    @media ${LARGE_UP} {
      font-size: 18px;
      line-height: 28px;
    }
  `,
  t5: css`
    font-family: ${TEXT_FONT};
    font-size: 14px;
    line-height: 20px;

    @media ${LARGE_UP} {
      font-size: 16px;
      line-height: 24px;
    }
  `,
  t6: css`
    font-family: ${TEXT_FONT};
    font-size: 13px;
    line-height: 20px;
  `,
  h1: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;
    font-size: 44px;
    line-height: 52px;

    @media ${MEDIUM} {
      font-size: 60px;
      line-height: 68px;
    }

    @media ${LARGE_UP} {
      font-size: 84px;
      line-height: 96px;
    }
  `,
  h2: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;

    font-size: 32px;
    line-height: 36px;

    @media ${MEDIUM} {
      font-size: 44px;
      line-height: 52px;
    }

    @media ${LARGE_UP} {
      font-size: 64px;
      line-height: 72px;
    }
  `,
  h3: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;

    font-size: 32px;
    line-height: 34px;

    @media ${MEDIUM} {
      font-size: 36px;
      line-height: 44px;
    }

    @media ${LARGE_UP} {
      font-size: 50px;
      line-height: 52px;
    }
  `,
  h4: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;

    font-size: 26px;
    line-height: 34px;

    @media ${MEDIUM} {
      font-size: 32px;
      line-height: 40px;
    }

    @media ${LARGE_UP} {
      font-size: 40px;
      line-height: 48px;
    }
  `,
  h5: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;

    font-size: 24px;
    line-height: 32px;

    @media ${MEDIUM} {
      font-size: 26px;
      line-height: 34px;
    }

    @media ${LARGE_UP} {
      font-size: 32px;
      line-height: 40px;
    }
  `,
  h6: css`
    font-family: ${HEADING_FONT};
    font-weight: bold;

    font-size: 20px;
    line-height: 28px;

    @media ${LARGE_UP} {
      font-size: 26px;
      line-height: 34px;
    }
  `,
}

const colors = {
  white: css`
    color: ${WHITE};
  `,
  black: css`
    color: ${BlACK};
  `,
  dark: css`
    color: ${DARK};
  `,
  silver: css`
    color: ${SILVER};
  `,
  grey: css`
    color: ${GREY};
  `,
  primary: css`
    color: ${PRIMARY};
  `,
  secondary: css`
    color: ${SECONDARY};
  `,
  secondaryAccent: css`
    color: ${SECONDARY_ACCENT};
  `,
  unset: undefined,
}

interface Props {
  style: keyof typeof styles
  children: React.ReactNode
  color?: keyof typeof colors
  className?: string
  bold?: boolean
  center?: boolean
  allCaps?: boolean
  html?: boolean
  inlineStyle?: React.CSSProperties
  as?: keyof JSX.IntrinsicElements
}

export const Text = ({
  as: Comp = "span",
  color = "dark",
  ...props
}: Props) => (
  <Comp
    className={cx(
      styles[props.style],
      colors[color],
      props.bold && BOLD_CLASS,
      props.center && CENTER_CLASS,
      props.allCaps && ALL_CAPS_CLASS,
      props.className
    )}
    style={props.inlineStyle}
    data-text-style={IS_DEV ? props.style : undefined}
  >
    {props.html ? (
      <span
        dangerouslySetInnerHTML={{
          __html: `${props.children}`,
        }}
      />
    ) : (
      props.children
    )}
  </Comp>
)
kylpo commented 4 years ago

https://rebassjs.org/reflexbox/#variants

image

kylpo commented 4 years ago

How to Use Block Variations in WordPress | CSS-Tricks

4Catalyzer/astroturf: An "artificial" CSS-in-JS for those that want it all.

function Button({ variant, children }) {
  return (
    <button
      variant={variant}
      css={css`
        color: black;
        border: 1px solid black;
        background-color: white;

        &.variant-primary {
          color: blue;
          border: 1px solid blue;
        }

        &.variant-secondary {
          color: green;
        }
      `}
    >
      {children}
    </button>
  );
}
kylpo commented 4 years ago

Stitches

variants: {
    color: {
      white: {
        backgroundColor: 'transparent',
        color: 'hsl(206,10%,44%)',
        ':hover': {
          backgroundColor: 'white',
          color: 'hsl(206,10%,5%)',
        },
      },
    }
  }
kylpo commented 4 years ago

Chakra uses base for its default:

<Box width={{ base: 1, sm: 1 / 2, md: 1 / 4 }} />

Chakra UI | Design System built with React

And variants defined in a config:

const theme = extendTheme({
  components: {
    Button: {
      // 1. We can update the base styles
      baseStyle: {
        fontWeight: "bold", // Normally, it's "semibold"
      },
      // 2. We can add a new button size or extend existing
      sizes: {
        xl: {
          h: "56px",
          fontSize: "lg",
          px: "32px",
        },
      },
      // 3. We can add a new visual variant
      variants: {
        "with-shadow": {
          bg: "red.400",
          boxShadow: "0 0 2px 2px #efdfde",
        },
        // 4. We can override existing variants
        solid: (props) => ({
          bg: "red.400",
        }),
      },
    },
  },
})

Customize Theme - Chakra UI

kylpo commented 3 years ago

Sitches has some cool ideas, too: Variants — Stitches

Including compoundVariant, boolean, and default variants.

The Future of the Front-End (with Pedro Duarte) – Stitches CSS-in-JS Demo - YouTube might be worth a re-watch.

kylpo commented 1 year ago

https://cva.style/docs/getting-started/variants

// components/button.ts
import { cva } from "class-variance-authority";

const button = cva("…", {
  variants: {
    intent: { primary: "…", secondary: "…" },
    size: { small: "…", medium: "…" },
  },
  compoundVariants: [
    // Applied via:
    //   `button({ intent: "primary", size: "medium" })`
    //     or
    //   `button({ intent: "secondary", size: "medium" })`
    {
      intent: ["primary", "secondary"],
      size: "medium",
      class: "…",
    },
  ],
});