Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional CSS with styled tag #409

Open
mattoni opened this issue May 9, 2019 · 6 comments
Open

Conditional CSS with styled tag #409

mattoni opened this issue May 9, 2019 · 6 comments
Labels
cat: extend syntax ✍ Support for a new syntax core team 🧬 Issues reserved for the core team feature: proposal 💬 New feature proposal that needs to be discussed platform: react 🛠️ Issue related to react

Comments

@mattoni
Copy link

mattoni commented May 9, 2019

Ask your Question

Is it possible to do something like the following?

const TopLevelList = styled.ul<{ dark?: boolean }>`
  border-top: 1px solid ${BrandColors.BORDER};
  margin-top: 0;

  ${props => props.dark && ({
    color: 'black',
  })}
`;

Typescript is giving the following error:

Argument of type '(props: { dark?: boolean | undefined; }) => false | { color: string; } | undefined' is not assignable to parameter of type 'string | number | CSSProperties | ((props: { dark?: boolean | undefined; }) => TLength)'.
  Type '(props: { dark?: boolean | undefined; }) => false | { color: string; } | undefined' is not assignable to type '(props: { dark?: boolean | undefined; }) => TLength'.
    Type 'false | { color: string; } | undefined' is not assignable to type 'TLength'.
      Type 'undefined' is not assignable to type 'TLength'.ts(2345)
@mattoni mattoni added the question Further information is requested label May 9, 2019
@mattoni
Copy link
Author

mattoni commented May 9, 2019

Trying to compile and ignore the TS error has linaria throwing this:

(60:6) Unknown word

  58 | `;
  59 | 
> 60 | const TopLevelList = styled.ul<{ dark?: boolean }>`
     |      ^
  61 |   border-top: 1px solid ${BrandColors.BORDER};
  62 |   margin-top: 0;

 @ ./.linaria-cache/src/components/api/attributes/AttributesList.linaria.css 2:14-189 21:1-42:3 22:19-194

@neozgul
Copy link

neozgul commented May 30, 2019

Hi.

I'm also wondering if there is a possibility to do this. With styled components we can have something like:

<Tab active={this.state.active} onClick={this.handleButton}></Tab>

const Tab = styled.button`
  width: 100%;
  outline: 0;
  border: 0;
  height: 100%;
  justify-content: center;
  align-items: center;
  line-height: 0.2;

  ${({ active }) => active && `
    background: blue;
  `}
`;

@brandonkal
Copy link

@neozgul This is currently not possible due to the way CSS is extracted at build time, the value of active is unknown at that time.

You'd have to do this:

<Tab className={cx(this.state.active && 'active')} onClick={this.handleButton}></Tab>

const Tab = styled.button`
  width: 100%;
  outline: 0;
  border: 0;
  height: 100%;
  justify-content: center;
  align-items: center;
  line-height: 0.2;
  &.active {
    background: blue;
  }
`;

With my proposal, you could define props inside styled, specify if the prop should be forwarded, and thus keep all logic inside the CSS as you had before:

<Tab active={this.state.active} onClick={this.handleButton}></Tab>

const Tab = styled.button`
  width: 100%;
  outline: 0;
  border: 0;
  height: 100%;
  justify-content: center;
  align-items: center;
  line-height: 0.2;
  &[props|active--] {
    background: blue;
  }
`;

@nhooyr
Copy link

nhooyr commented Jul 3, 2019

The readme has an example that shows you can do this:

import { styled } from 'linaria/react';
import { families, sizes } from './fonts';

// Write your styles in `styled` tag
const Title = styled.h1`
  font-family: ${families.serif};
`;

const Container = styled.div`
  font-size: ${sizes.medium}px;
  color: ${props => props.color};
  border: 1px solid red;

  &:hover {
    border-color: blue;
  }

  ${Title} {
    margin-bottom: 24px;
  }
`;

// Then use the resulting component
<Container color="#333">
  <Title>Hello world</Title>
</Container>;

@brandonkal
Copy link

Yes. That is the other approach.

Be careful though @nhooyr. You'll notice color will leak into the DOM. The color attribute has special meaning in Safari. I've started just suffixing custom props that shouldn't make it to the DOM. So "color" would become "color$"

@daviestar
Copy link

daviestar commented Mar 3, 2020

No support for conditional CSS using the styled tag is the biggest hurdle for us moving to linaria from styled-components in a typescript project, however as suggested by @brandonkal I think something like this:

import React from "react";
import classnames from "classnames";
import { styled } from "linaria/react";

function Button() {
  const [active, setActive] = React.useState(false);
  return (
    <StyledButton
      className={classnames({ active })}
      onClick={() => setActive(true)}
    />
  );
}

const StyledButton = styled.button`
  background: grey;
  &.active {
    background: blue;
  }
`;

which prevents "proper" React props unintentionally leaking to the DOM, something I've had issues with before, could be a nice solution (could even be the nicest solution!).

The tradeoff is losing type checking for the conditional styling, which may or may not be a big deal in your project.

@jayu jayu added cat: extend syntax ✍ Support for a new syntax feature: proposal 💬 New feature proposal that needs to be discussed platform: react 🛠️ Issue related to react core team 🧬 Issues reserved for the core team and removed question Further information is requested labels Apr 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cat: extend syntax ✍ Support for a new syntax core team 🧬 Issues reserved for the core team feature: proposal 💬 New feature proposal that needs to be discussed platform: react 🛠️ Issue related to react
Projects
None yet
Development

No branches or pull requests

6 participants