Styling tools

Styling tools

tools for external stylesheets:

  • classnames package
  • CSS modules
  • SCSS

CSS-in-JS libraries:

  • emotion
  • styled-components
  • linaria
  • ...

classnames

utility: npm package classnames:

import classNames from 'classnames';

<div
  className={classNames({
    todoitem: true,
    completed: props.completed,
  })}
>
  [...]
</div>;

CSS modules

CSS modules are preconfigured in create-react-app; they enable using CSS class names that are guaranteed to be unique across CSS files

import styles from './TodoItem.module.css';

<div className={styles.todoItem + ' ' + styles.completed}>
  ...
</div>;

possible output in the HTML:

<div
  class="TodoItem_todoItem__L9V1E TodoItem_completed__9bPW1"
>
  ...
</div>

CSS modules and classnames

<div className={classNames({
  [styles.todoitem]: true,
  [styles.completed]: props.completed
})}>

CSS-in-JS

CSS-in-JS: JavaScript is used to generate and attach stylesheets

Nowadays it's considered ok to put styling in the same file as JavaScript / HTML

CSS-in-JS

libraries:

  • emotion
  • styled-components
  • linaria
  • ...

CSS-in-JS

basic example in emotion:

import { css } from '@emotion/css';

<button
  className={css({
    color: 'blue',
    '@media (min-width: 600px)': { color: 'green' },
    '&:hover': { color: 'red' },
  })}
>
  foo
</button>;

CSS-in-JS

alternative notation: via "tagged template strings"

<button
  className={css`
    color: blue;
    &:hover {
      color: red;
    }
  `}
>
  foo
</button>

CSS-in-JS

recommended: use the css property instead of className (requires an extra source transform)

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

<button css={css({ color: 'blue' })}>foo</button>;

Styled components

styled components: approach where components are created whose only task is adding styling to HTML elements

example: PrimaryButton = a button element with extra styling

Styled components

creating a styled component with emotion:

import styled from '@emotion/styled';

const PrimaryButton = styled.button({
  color: 'blue',
  '&:hover': { color: 'red' },
});

Styled components

theoretical manual version:

function PrimaryButton(props) {
  return (
    <button
      {...props}
      className={css({
        color: 'blue',
        '&:hover': { color: 'red' },
      })}
    />
  );
}

Styled components

dynamic styles via props:

with JavaScript:

const Button = styled.button({
  padding: 8,
  color: (props) => (props.primary ? 'blue' : 'black'),
});

with TypeScript:

const Button = styled.button<{ primary: boolean }>({
  padding: 8,
  color: (props) => (props.primary ? 'blue' : 'black'),
});

Exercise

exercise: use some styling tools to add extra styling to an existing application (e.g. to the slideshow)

Animations

Animations

  • basic animations: via CSS transitions

animating appearance / disappearance of elements:

  • react-transition-group (based on CSS class names)

advanced libraries:

  • react-spring
  • framer motion

Animations

basic animations: via CSS transitions

.TodoItem {
  background-color: salmon;
  transition-property: background-color;
  transition-duration: 0.5s;
}

.TodoItem--Completed {
  background-color: lightgrey;
}

Appearance / disappearance of elements

phases for appearance / disappearance of elements:

  • unmounted
  • before entering
  • entering
  • displayed
  • before exiting
  • exiting

Appearance / disappearance of elements

react-transition-group: based on CSS class names

Appearance / disappearance of elements

example with framer motion:

<AnimatePresence>
  {hovered ? (
    <motion.button
      animate={{ opacity: 1 }}
      initial={{ opacity: 0 }}
      exit={{ opacity: 0 }}
      onClick={() => props.onDelete(props.todo.id)}
    >
      delete
    </motion.button>
  ) : null}
</AnimatePresence>

Appearance / disappearance of elements

example with framer motion:

<AnimatePresence>
  {todos.map((todo) => (
    <motion.li
      key={todo.id}
      animate={visibleStyle}
      initial={hiddenStyle}
      exit={hiddenStyle}
    >
      {todo.title}
    </motion.li>
  ))}
</AnimatePresence>

Appearance / disappearance of elements

example with react-spring:

<Transition
  items={hovered}
  from={{ opacity: 0 }}
  enter={{ opacity: 1 }}
  leave={{ opacity: 0 }}
  reverse={hovered}
>
  {(style, hovered) =>
    hovered ? (
      <animated.button
        style={style}
        onClick={() => props.onDelete(props.todo.id)}
      >
        delete
      </animated.button>
    ) : null
  }
</Transition>