Code Monkey home page Code Monkey logo

styled-tools's Introduction

styled-tools 💅

NPM version NPM downloads Dependencies Build Status Coverage Status

Useful interpolated functions for styled-components 💅, emotion 👩‍🎤, JSS and other CSS-in-JS libraries.

Install

npm:

npm i styled-tools

Yarn:

yarn add styled-tools

Usage

import styled, { css } from "styled-components";
import { prop, ifProp, switchProp } from "styled-tools";

const Button = styled.button`
  color: ${prop("color", "red")};
  font-size: ${ifProp({ size: "large" }, "20px", "14px")};
  background-color: ${switchProp("theme", {
    dark: "blue", 
    darker: "mediumblue", 
    darkest: "darkblue" 
  })};
`;

// renders with color: blue
<Button color="blue" />

// renders with color: red
<Button />

// renders with font-size: 20px
<Button size="large" />

// renders with background-color: mediumblue
<Button theme="darker" />

A more complex example:

const Button = styled.button`
  color: ${prop("theme.colors.white", "#fff")};
  font-size: ${ifProp({ size: "large" }, prop("theme.sizes.lg", "20px"), prop("theme.sizes.md", "14px"))};
  background-color: ${prop("theme.colors.black", "#000")};
  
  ${switchProp("kind", {
    dark: css`
      background-color: ${prop("theme.colors.blue", "blue")};
      border: 1px solid ${prop("theme.colors.blue", "blue")};
    `,
    darker: css`
      background-color: ${prop("theme.colors.mediumblue", "mediumblue")};
      border: 1px solid ${prop("theme.colors.mediumblue", "mediumblue")};
    `,
    darkest: css`
      background-color: ${prop("theme.colors.darkblue", "darkblue")};
      border: 1px solid ${prop("theme.colors.darkblue", "darkblue")};
    `
  })}
  
  ${ifProp("disabled", css`
    background-color: ${prop("theme.colors.gray", "#999")};
    border-color: ${prop("theme.colors.gray", "#999")};
    pointer-events: none;
  `)}
`;

API

Table of Contents

prop

Returns the value of props[path] or defaultValue

Parameters

  • path string
  • defaultValue any

Examples

import styled from "styled-components";
import { prop } from "styled-tools";

const Button = styled.button`
  color: ${prop("color", "red")};
`;

Returns PropsFn

theme

Same as prop, except that it returns props.theme[path] instead of props[path].

Parameters

  • path string
  • defaultValue any

Examples

import styled from "styled-components";
import { theme } from "styled-tools";

const Button = styled.button`
 color: ${theme("button.color", "red")};
`;

palette

Returns props.theme.palette[key || props.palette][tone || props.tone || 0] or defaultValue.

Parameters

  • keyOrTone (string | number)
  • toneOrDefaultValue any
  • defaultValue any

Examples

import styled, { ThemeProvider } from "styled-components";
import { palette } from "styled-tools";

const theme = {
  palette: {
    primary: ['#1976d2', '#2196f3', '#71bcf7', '#c2e2fb'],
    secondary: ['#c2185b', '#e91e63', '#f06292', '#f8bbd0']
  }
};

const Button = styled.button`
  color: ${palette(1)};                    // props.theme.palette[props.palette][1]
  color: ${palette("primary", 1)};         // props.theme.palette.primary[1]
  color: ${palette("primary")};            // props.theme.palette.primary[props.tone || 0]
  color: ${palette("primary", -1)};        // props.theme.palette.primary[3]
  color: ${palette("primary", 10)};        // props.theme.palette.primary[3]
  color: ${palette("primary", -10)};       // props.theme.palette.primary[0]
  color: ${palette("primary", 0, "red")};  // props.theme.palette.primary[0] || red
`;

<ThemeProvider theme={theme}>
  <Button palette="secondary" />
</ThemeProvider>

ifProp

Returns pass if prop is truthy. Otherwise returns fail

Parameters

Examples

import styled from "styled-components";
import { ifProp, palette } from "styled-tools";

const Button = styled.button`
  background-color: ${ifProp("transparent", "transparent", palette(0))};
  color: ${ifProp(["transparent", "accent"], palette("secondary"))};
  font-size: ${ifProp({ size: "large" }, "20px", ifProp({ size: "medium" }, "16px", "12px"))};
`;

Returns PropsFn

ifNotProp

Returns pass if prop is falsy. Otherwise returns fail

Parameters

Examples

import styled from "styled-components";
import { ifNotProp } from "styled-tools";

const Button = styled.button`
  font-size: ${ifNotProp("large", "20px", "30px")};
`;

Returns PropsFn

withProp

Calls a function passing properties values as arguments.

Parameters

Examples

// example with polished
import styled from "styled-components";
import { darken } from "polished";
import { withProp, prop } from "styled-tools";

const Button = styled.button`
  border-color: ${withProp(prop("theme.primaryColor", "blue"), darken(0.5))};
  font-size: ${withProp("theme.size", size => `${size + 1}px`)};
  background: ${withProp(["foo", "bar"], (foo, bar) => `${foo}${bar}`)};
`;

Returns PropsFn

switchProp

Switches on a given prop. Returns the value or function for a given prop value. Third parameter is default value.

Parameters

Examples

import styled, { css } from "styled-components";
import { switchProp, prop } from "styled-tools";

const Button = styled.button`
  font-size: ${switchProp(prop("size", "medium"), {
    small: prop("theme.sizes.sm", "12px"),
    medium: prop("theme.sizes.md", "16px"),
    large: prop("theme.sizes.lg", "20px")
  }, prop("theme.sizes.md", "16px"))};
  ${switchProp("theme.kind", {
    light: css`
      color: LightBlue;
    `,
    dark: css`
      color: DarkBlue;
    `
  }, css`color: black;`)}
`;

<Button size="large" theme={{ kind: "light" }} />

Returns PropsFn

Types

Needle

A Needle is used to map the props to a value. This can either be done with a path string "theme.size.sm" or with a function (props) => props.theme.size.sm (these two examples are equivalent).

All of styled-tools can be used as Needles making it possible to do composition between functions. ie ifProp(theme("dark"), "black", "white")

Type: (string | Function)

License

MIT © Diego Haz

styled-tools's People

Contributors

dependabot[bot] avatar diegohaz avatar divyagnan avatar greenkeeper[bot] avatar hnordt avatar jtmthf avatar marhalpert avatar mehrad77 avatar miketamis avatar nderscore avatar stevenbenisek avatar tauka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

styled-tools's Issues

How to get the {palette}Text color ?

Hello, with a simple palette:

export default {
    palette: {
        primary: "#6ab04c",
        primaryText: "#fff",
    }
}

If I set the palette prop to a component, as your doc mentioned, I can use background: ${palette()}; to get the palette color. But how can I get the text color? without doing:

background: ${props => props.theme.palette[`${props.palette}Text`]};

The following does not work of course:
background: ${palette(prop(`${props => props.palette)Text`};

Thank you very much

unable to interpolate ${prop('__')}

Trying to make an Icon component a little more modular by allowing a prop for palette level to be passed. Here is the original:

  color: ${ifProp('palette', palette({ grayscale: 0 }, 3), 'currentcolor')};

and here is what I am trying to change it to:

  color: ${ifProp('palette', palette({ grayscale: 0 }, **prop('paletteLevel')**), 'currentcolor')};

however this doesn't work for me. Should the function prop('paletteLevel') not evaluate to the prop I pass in (2 in this case)?

Styled-tools not working

I am trying to use switchProp but for some reason values are empty. http://jmp.sh/mEP6Wtm

PopupButton.defaultProps = {
  titleAlign: 'left',
}

const Container = styled.View`
  flex: 1;
`

const Button = styled.TouchableHighlight`
  flex:1;
  justify-content:center;
  align-items: ${switchProp('titleAlign', {
    center: 'center',
    left: 'flex-start',
  })};
  padding-top: ${palette.SXL};
  padding-bottom: ${palette.SXL};

`

Any idea where the problem might be?

Confusing example

In this example:

const Button = styled.button`
  color: ${prop('theme.colors.white', '#fff')};
  font-size: ${ifProp({ size: 'large' }, prop('theme.sizes.lg', '20px'), prop('theme.sizes.md', '14px'))};
  background-color: ${prop('tgeme.colors.black', '#000')};
  
  ${ifProp('disabled', css`
    background-color: ${prop('theme.colors.gray', '#999')};
    pointer-events: none;
  `)}
`

we have ${ifProp('disabled', css but css is neither defined nor needed.

So it should be like that:

const Button = styled.button`
  color: ${prop('theme.colors.white', '#fff')};
  font-size: ${ifProp({ size: 'large' }, prop('theme.sizes.lg', '20px'), prop('theme.sizes.md', '14px'))};
  background-color: ${prop('tgeme.colors.black', '#000')};
  
  ${ifProp('disabled', `
    background-color: ${prop('theme.colors.gray', '#999')};
    pointer-events: none;
  `)}
`

Needles should be recursively resolved

I ended up finding a solution to my problem while writing this issue, but I believe it's something that styled-tools should handle by default. I'm working with the sample below which a reakit theme, and the issue is in contrastText. What ends up happening is that bg resolves to a prop-getter function instead of a string. The reason being that bgNeedle is a prop-getter that resolves to a prop-getter which is not supported by withProp. What I ended up doing is writing a custom implementation of withProp (below the example) that recursively resolves prop-getter functions. It would be useful if this was either the default behavior of withProp or was exposed as a separate function.

// utils.ts

import { range } from 'lodash-es';
import { getLuminance, lighten } from 'polished';
import { Needle, palette as p, withProp } from 'styled-tools';

export const contrastText = (bgNeedle: Needle<any>) =>
  withProp(
    [bgNeedle, p('black'), p('white')] as any,
    (bg: string, black: string, white: string) => {
      // bg ends up being a prop getter function here instead of a string.
      return getLuminance(bg) > 0.179 ? black : white;
    },
  );

export const contrastPalette = (palette: string, tone?: number) =>
  contrastText(p(palette, tone));

export const lightenPalette = (
  amount: number,
  palette: string,
  tone?: number,
) =>
  withProp([p(palette, tone)] as any, (color: string) =>
    lighten(amount, color),
  );

export const tonePalette = (palette: string) => [
  p(palette),
  ...range(0.1, 0.5, 0.1).map(i => lightenPalette(i, palette)),
];

export const tonePaletteText = (palette: string) =>
  range(5).map(i => contrastPalette(palette, i));

export const tonePaletteWithText = (name: string, palette: string) => ({
  [name]: tonePalette(palette),
  [`${name}Text`]: tonePaletteText(name)
});
// index.ts

import { tonePaletteWithText } from './utils';

export const palette = {
  white: '#fff',
  black: '#000',

  astronautBlue: '#003a5d',

  ...tonePaletteWithText('primary', 'astronautBlue'),
};

Solution

// simple deepWithProp

const deepWithProp = <Props extends object, T = undefined>(
  needle: Needle<Props> | Needle<Props>[],
  fn: (...args: any[]) => T
) => (props = {}): T => {
  if (Array.isArray(needle)) {
    const needles = needle.map(arg => deepWithProp(arg, x => x)(props));
    return fn(...needles);
  }
  if (typeof needle === "function") {
    return fn(deepWithProp(needle(props as Props), x => x)(props));
  }
  return fn(prop(needle, needle)(props));
};

Improve Palette Types

Bug In Documentation

As of now it lists that the second argument for palette is always any, but when there are three arguments the type interfaces enforces the second argument to be of type number. This can be better labeled in the documentation/readme
Screen Shot 2020-01-16 at 4 23 09 PM

Feature Request

It seems a bit weird to switch on the expected types, where the second value can be either the index or the default value. A possible extension of this could be to actually allow for string | number for both the first and second arguments and to declare the default as the prop within the component.

palette(arg1) => props.theme.palette[arg1] || defaultColor
palette(arg1, arg2) => props.theme.palette[arg1][arg2] || defaultColor
palette(arg1, arg2, arg3) => props.theme.palette[arg1]?.[arg2] || arg3

<ThemeProvider>
  <SomeElement defaultColor="#000000" />
</ThemeProvider>

What this allows is for each argument to be either a string or number, and to access either an array or an object in the same way. This gives for some nicer formatting of palette when there are several different color organization options, in addition to extending the model to support more options.

const theme = {
  palette: {
    solids: {
      black: '#000000',
      white: '#FFFFFF',
    },
    gradients: {
      blue: 'linear-gradient(135deg, #75F094 0%, #75B2F0 100%)',
    }
  }
}

If going with the latter solution, the documentation issue is moot and can be ignored.

theme usage with darken from polished

Hey there!

Thanks for a great package full of lovely tools!

I'm running into an issue I don't quite understand:

background: ${darken(0.1, theme("green", "red")};

causes a TypeScript error (in my TS codebase):

[ts] Argument of type '<Props, Theme extends { [key: string]: any; }>(props: Props & { theme: Theme; }) => string | Theme[keyof Theme]' is not assignable to parameter of type 'string'.

The best I can figure out is that darkens types (darken(amount: string | number, color: string): string) expect a string, but theme isn't necessarily giving a string (it could be giving Theme[keyof Theme]). Would you have any understanding of how to solve this problem?

Thanks!

image

invalid valid for prop 'color' on <span /> tag

I'm trying to pass a palette color as a prop, and everything is working fine in my program, but I'm getting this error:

Warning: Invalid value for prop color on tag. Either remove it from the element, or pass a string or number value to keep it in the DOM

here is the styled component:

const ColoredText = styled.span`
  font-family: ${font('cursive')};
  color: ${props => props.color};
`

and here is where I am rendering it:

<ColoredText color={palette('secondary', 2)}>way</ColoredText>

is this error something to be concerned about, or can I safely ignore it? The text is rendering the color as I'd expect

prop() not setting default

First off, thanks for the great library! Really cleans up a lot of the boilerplate in our styled components 👍

I've currently got the following code:

const Container = styled.div`
  height: ${prop('height', 'auto')};
`

then elsewhere something is using this:

const Component = ({ height }) => (
 <Container height={height} />
)

Component.defaultProps = {
  height: null
}

Passing something like 400px into the height prop works fine, but not passing anything gives me this when it's rendered:
screen shot 2017-09-22 at 11 55 08 am

Is this not working properly or am I missing something obvious? Should it not be rendering height: auto;?

Thanks again!

An in-range update of babel-eslint is breaking the build 🚨

Version 7.2.1 of babel-eslint just got published.

Branch Build failing 🚨
Dependency babel-eslint
Current Version 7.2.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As babel-eslint is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪


Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
Commits

The new version differs by 6 commits .

  • 3cda62e 7.2.1
  • 5626de1 Remove left over eslint 2 estraverse code (#452)
  • b5fb53b Fix type param and interface declaration scoping (#449)
  • f1cee0f Remove lodash dependency (#450)
  • eb05812 Format non-regression errors for legibility (#451)
  • 7972a05 Update README.md with codeFrame option (#448)

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Add switchProp

Suggested on Twitter: https://twitter.com/djungst/status/883903002787987457

Something like this?

import styled, { css } from 'styled-components'
import { prop, switchProp } from 'styled-tools'

const Button = styled.button`
  font-size: ${switchProp('size', {
    small: prop('theme.sizes.sm', '12px'),
    large: prop('theme.sizes.lg', '20px'),
    default: prop('theme.sizes.md', '16px'),
  })};
  ${switchProp('theme.kind', {
    light: css`
      color: ...
    `,
    dark: css`
      ...
    `,
    default: ...
  })}
`

<Button size="large" theme={{ kind: 'light' }} />

Drop support for array needle

It's not consistent between prop and ifProp for example. (ifProp should be kept though).

Also, we need to start supporting multiple props in withProp

Named exports not detected when using Rollup

Issue

Bundling styled-tools with Rollup produces the following error:

[!] Error: 'prop' is not exported by node_modules/styled-tools/index.js
https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module

As per https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module and https://github.com/rollup/rollup-plugin-commonjs#custom-named-exports this can be solved by using custom named exports.

However, the better solution would be to create a package that exports not only a CommonJS but also an ES Modules version. This would avoid the extra Rollup config and have the added benefit of tree shaking

Test case

In order to reproduce the issue you need a new project with these files:

  • package.json
  • rollup.config.js
  • index.js

From the root of the project run:

npm install && npm run build

package.json

{
  "scripts": {
    "build": "rollup -c"
  },
  "devDependencies": {
    "rollup": "^0.55.0",
    "rollup-plugin-node-resolve": "^3.0.2"
  },
  "dependencies": {
    "styled-tools": "^0.2.3"
  }
}

rollup.config.js

import resolve from 'rollup-plugin-node-resolve';

export default {
  input: 'index.js',
  output: {
    format: 'es',
  },
  plugins: [resolve()],
};

index.js

import { prop } from 'styled-tools';

prop('color', 'red');

Transpiling the src to ES5

I'm using styled-tools within my project (using your ARc boilerplate), and noticed that in your .babelrc you are just targeting the last 2 browsers. When I try to build my app, it ignores /node_modules/ so it won't try and transpile the source code of this package. Because this package is not transpiling to ES5, UglifyJS is throwing an error because it cannot read the source code and the build will fail. Can you update this package to transpile to ES5? Will also have the same issue with styled-theme.

Add default case option to switchProp

I think it might be nice to have a default case option for the switchProp() function. Having a generic else case without having to specifically write out every other possible prop value would be nice. Plus, it makes it closer to mirroring the functionality of an actual switch statement! 😉

I think adding a third optional argument (identical to how prop() 's defaultCase argument works) would be fine, as it wouldn't break backwards compatibility with current usage of the function.

Add comments to the typescript definitions [enhancement]

I think it would be nice to add comments to the typescript definitions, using the same documentation as is in this repository. I already did it on local but I don't have the permissions for PR's of course.

This is for the usage with VScode, following the TS doc format:
スクリーンショット 2019-07-25 9 57 19

An in-range update of eslint-plugin-import is breaking the build 🚨

Version 2.4.0 of eslint-plugin-import just got published.

Branch Build failing 🚨
Dependency eslint-plugin-import
Current Version 2.3.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As eslint-plugin-import is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪

Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details

Commits

The new version differs by 10 commits.

  • 44ca158 update utils changelog
  • a3728d7 bump eslint-module-utils to v2.1.0
  • 3e29169 bump v2.4.0
  • ea9c92c Merge pull request #737 from kevin940726/master
  • 8f9b403 fix typos, enforce type of array of strings in allow option
  • 95315e0 update CHANGELOG.md
  • 28e1623 eslint-module-utils: filePath in parserOptions (#840)
  • 2f690b4 update CI to build on Node 6+7 (#846)
  • 7d41745 write doc, add two more tests
  • dedfb11 add allow glob for rule no-unassigned-import, fix #671

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

TS error when using prop with ThemedStyledComponentsModule

I'm trying to use prop within ifProp, but getting an error that:

[ts]
Argument of type '<Props>(props?: Props) => Props[keyof Props]' is not assignable to parameter of type 'Interpolation<ThemeProps<ThemeInterface>>'.
  Type '<Props>(props?: Props) => Props[keyof Props]' is not assignable to type 'InterpolationFunction<ThemeProps<ThemeInterface>>'.
    Type 'ThemeInterface' is not assignable to type 'Interpolation<ThemeProps<ThemeInterface>>'.
      Type 'ThemeInterface' is not assignable to type 'ReadonlyArray<string | number | false | Styles | Keyframes | StyledComponentClass<any, any, any> | InterpolationFunction<ThemeProps<ThemeInterface>> | ReadonlyArray<FlattenInterpolation<ThemeProps<ThemeInterface>>>>'.
        Property 'length' is missing in type 'ThemeInterface'. [2345]
interface SpaceProps {
  around?: string;
  bottom?: string;
  left?: string;
  right?: string;
  top?: string;
}

export const Space = styled.div<SpaceProps>`
  ${ifProp(
    'around',
    css`
      margin: ${prop('around')}em;
    `
  )};
  ${ifProp(
    'top',
    css`
      margin-top: ${prop('top')}em;
    `
  )};
  ${ifProp(
    'right',
    css`
      margin-right: ${prop('right')}em;
    `
  )};
  ${ifProp(
    'bottom',
    css`
      margin-bottom: ${prop('bottom')}em;
    `
  )};
  ${ifProp(
    'left',
    css`
      margin-left: ${prop('left')}em;
    `
  )};
`;

I'm assuming I'm doing something wrong, but can't quite pinpoint what the problem may be.

Heads-up: 1.7.0 was a breaking change for us

Hi ! I just want to share this for other people who will run into a similar issue.

We use code similar to this (simplified):

const StyledElement = styled.div`
  cursor: ${ifProp('onClick', 'pointer', 'inherit')}
`

This worked fine with 1.6.0, but in 1.7.0 we had some weird issue: our router was navigating to the wrong page, seemingly with no reason. Turns out this is because styled-tools will now call the our onClick prop (or anything that looks like a function) i n relation to this new feature: #55
So in our case, it was calling a route change with improper parameters (something like /user/undefined), simply by rendering a component.

We fixed it by reverting to the standard styled-components callback:

const StyledElement = styled.div`
  cursor: ${({ onClick }) => onClick ? 'pointer' : 'inherit'}
`

We could also have ensured the prop we use is a boolean (perhaps using an additional prop).

This may impact a few places in our code, so we're thinking of making our own simple version of ifProp that respects the legacy behaviour, so that changing our existing code is easier.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.