Reyno
Reyno

Reputation: 6505

Merge a single CSS property in Emotion

I'm trying to create reusable animations in Emotion but have run into some issues. I've defined an animation for a fadeUp effect which works fine.

export const animatedFadeUp = css`
  opacity: 1;
  transform: translateY(0);
`;

export const fadeUp = css`
  opacity: 0;
  transform: translateY(var(--spacing-medium));
  transition: opacity 0.5s ease-in-out,
    transform 0.5s ease-in-out;

  html.no-js & {
    ${animatedFadeUp}
  }
`;

But when i try to apply the fadeUp animation to an element that already has the transition defined it gets overruled. For example the button below. The transition will be overruled by the button.

const Button = styled.button`
  ${fadeUp}
  background: orange;
  transition: background-color 0.5s ease-in-out;

  &:hover,
  &:focus {
    background: gold;
  }
`;

Is there a solution to combine only a single property? Something like:

const Button = styled.button`
  ${fadeUp}
  background: orange;
  transition: ${fadeUp.transition},
    background-color 0.5s ease-in-out;

  &:hover,
  &:focus {
    background: gold;
  }
`;

Upvotes: 6

Views: 3814

Answers (2)

Pushkin
Pushkin

Reputation: 3604

Instead of passing it as a CSS tagged template literals or using styled components, you may use it as CSS object. By using as such, it would be more flexible and easier to refer their properties.

<p
  css={{
    color: 'red'
 }}
>Hello World</p>

So your code would become

export const animatedFadeUp = {
  opacity: 1,
  transform: 'translateY(0)',
};

export const fadeUp = {
  opacity: 0,
  transform: 'translateY(var(--spacing-medium))',
  transition: `opacity 0.5s ease-in-out,
    transform 0.5s ease-in-out`,

  ['html.no-js &']: {
    ...animatedFadeUp
  }
};

const App = () => (
  <button
    css={{
      ...fadeUp,
      background: 'orange',
      transition: `${fadeUp.transition},
        background-color 0.5s ease-in-out`,

      ['&:hover']: {
        background: 'gold',
      },
      ['&:focus']: {
        background: 'gold',
      }
    }}
  >Click Me</button>
)

Upvotes: 4

Ray Chan
Ray Chan

Reputation: 1180

Not quite familiar with the Emotion library, maybe there is an API that exposes style value access like you tried with ${fadeUp.transition}. But I won't be surprised if there is no such API as it increases the complexity of the library. However, hope is not lost, without knowing much of the API, we can still solve this problem using language features supported by javascript. Below is an example:

import React, { useState } from 'react';
import styled from '@emotion/styled';
import { css } from '@emotion/react';

export const animatedFadeUp = css`
  opacity: 1;
  transform: translateY(0);
`;

// Notice that the annoymous function is invoked immediately to get back an object
// with properties of `css` and `transition`, so that we can access them later
export const fadeUp = (() => {
  // By defining the transition value here, we can update the transition value in one
  // place instead of updating in multiple places, including the downstream consumers
  // of the returned object
  const transition = 'opacity 0.5s ease-in-out, transform 0.5s ease-in-out';

  return {
    css: css`
      opacity: 0;
      transform: translateY(25px);
      transition: ${transition};

      &.active {
        ${animatedFadeUp}
      }
    `,
    transition,
  };
})();

// Note that we need to access the `css` property explicitly here
// like this - ${fadeUp.css}
const Button = styled.button`
  ${fadeUp.css}
  background: green;
  transition: ${fadeUp.transition}, background-color 0.5s ease-in-out;

  &:hover,
  &:focus,
  &.active {
    background: gold;
  }
`;

function App() {
  const [className, setClassName] = useState('');

  return (
    <div className="App">
      <Button className={className}>This my button component.</Button>

      <button onClick={() => setClassName(className ? '' : 'active')}>
        Toggle
      </button>
    </div>
  );
}

export default App;

In short, we create an object that holds the information of the css and transition so that we can use it later. The downside of this approach is that we need to remember that when using fadeUp, we have to treat it differently by explicitly accessing fadeUp.css, unlike other emotion created css.

Upvotes: 0

Related Questions