Graduate Program KB

Component Architecture

Styled Components

  • Separates css into modules to assist with our scoping and specificity issues.
  • Have server side rendering support.
  • Uses tagged template literals.
  • See below we pass the styled.button function a template string as an argument.
const Button  = styled.button`
    font-size: 32px;
`;
  • We can reference the soon to be created class with &:
const Button = styled.button`
    display: flex;

    &:hover {
        color:red;
    }
`;
  • The figure element, along with some other useful elements (cite and figcaption) is useful to group information together, especially when there is info you don't want to be seen.

        <figure>
            <QuoteContent>{content}</QuoteContent>
            <figcaption>
                <cite>
                    <a href={source}>{by}</a>
                </cite>
            </figcaption>
        </figure>
    

Useful Pseudo elements

  • before: can be used along with the content property to place things before the content within the tag.
  • after: same as above but does it after.
  • NOTE: Extremely useful for having content in blocks but not allowing them to be selected (purely decorative).

To Use Style Components

  • Installation
npm install styled-components
  • In order for react to generate meaningful class names for development rather than just a hash you can install the following plugin, note that this is only in development, they will still just be hashes in production for performance reasons.
npm install --save-dev babel-plugin-styled-components
  • In the React application:
// Change this:
import styled from 'styled-components'

// To this:
import styled from 'styled-components/macro'

Global Styles

  • There is a specific API for creating global styles:
    // GlobalStyles.js
    import { createGlobalStyle } from 'styled-components';

    const GlobalStyles = createGlobalStyle`
    *, *::before, *::after {
        box-sizing: border-box;
    }

    html {
        font-size: 1.125rem;
    }

    body {
        background-color: hsl(0deg 0% 95%);
    }
    `;

    export default GlobalStyles;
  • Below is how we can use this in a React App, notice that it doesn't matter where we render it in the app as it renders the global styles to the <head>.
    // App.js
    import GlobalStyles from './GlobalStyles';

    function App() {
    return (
        <Wrapper>
        <Router>
            {/* An entire app here! */}
        </Router>

        <GlobalStyles />
        </Wrapper>
    )
    }

    export default App;

Dynamic Styling

  • We can style dynamically by the use of props and accessing them via our template literals.
const Button = {{ color, onClick, children }} => {
    return (
        <Wrapper onClick={onClick} color={color}>
            {children}
        </Wrapper>
    );
}

const Wrapper = styled.button`
    color: ${props => props.color};
    padding: 16px 24px;
`;

render(<Button color="red"> Hello </Button>)
  • There is a cool way to do this with the var keyword in css
const Button = {{ color, onClick, children }} => {
    return (
        <Wrapper
            onClick={onClick}
            style={{ '--hover-color': color }}
        >
            {children}
        </Wrapper>
    );
}

const Wrapper = styled.button`
    padding: 16px 24px;

    &:hover {
        color: var(--hover-color);
    }
`;


render(<Button color="red"> Hello </Button>)

Composition

  • We can use a Base component to style off of if we have different but similar components.
import styled from 'styled-components'

export default function App() {
  return <PrimaryButton>Button</PrimaryButton>
}

const Base = styled.button`
  font-size: 21px;
`

const PrimaryButton = styled(Base)`
  background: blue;
  color: white;
`

Cool Exercise Snippet

  • code from the button exercise, I liked a lot of his techniques here:
import React from 'react'
import styled from 'styled-components'

import { COLORS } from './constants'

const SIZES = {
  small: {
    '--borderRadius': 2 + 'px',
    '--fontSize': 16 / 16 + 'rem',
    '--padding': '6px 12px',
  },
  medium: {
    '--borderRadius': 2 + 'px',
    '--fontSize': 18 / 16 + 'rem',
    '--padding': '14px 20px',
  },
  large: {
    '--borderRadius': 4 + 'px',
    '--fontSize': 21 / 16 + 'rem',
    '--padding': '18px 32px',
  },
}

const Button = ({ variant, size, children }) => {
  const styles = SIZES[size]

  let Component
  if (variant === 'fill') {
    Component = FillButton
  } else if (variant === 'outline') {
    Component = OutlineButton
  } else if (variant === 'ghost') {
    Component = GhostButton
  } else {
    throw new Error(`Unrecognized Button variant: ${variant}`)
  }

  return <Component style={styles}>{children}</Component>
}

const ButtonBase = styled.button`
  font-size: var(--fontSize);
  font-family: 'Roboto', sans-serif;
  padding: var(--padding);
  border-radius: var(--borderRadius);
  border: 2px solid transparent;

  &:focus {
    outline-color: ${COLORS.primary};
    outline-offset: 4px;
  }
`

const FillButton = styled(ButtonBase)`
  background-color: ${COLORS.primary};
  color: ${COLORS.white};

  &:hover {
    background-color: ${COLORS.primaryLight};
  }
`
const OutlineButton = styled(ButtonBase)`
  background-color: ${COLORS.white};
  color: ${COLORS.primary};
  border: 2px solid currentColor;

  &:hover {
    background-color: ${COLORS.offwhite};
  }
`
const GhostButton = styled(ButtonBase)`
  color: ${COLORS.gray};
  background-color: transparent;

  &:focus {
    outline-color: ${COLORS.gray};
  }

  &:hover {
    background: ${COLORS.transparentGray15};
    color: ${COLORS.black};
  }
`

export default Button

as Tag

  • as can be used to treat components differently.
  • A common example is to use as='a' to treat a button as a link.
import styled from 'styled-components'

function Button({ href, children }) {
  return (
    <Wrapper href={href} as={href ? 'a' : 'button'}>
      {children}
    </Wrapper>
  )
}

const Wrapper = styled.button`
  background: blue;
  color: white;
  border: none;
  padding: 16px 24px;
  border-radius: 4px;
`

const App = () => <Button href="/">Hello</Button>

export default App

Escape Hatches

  • Your components should follow some convention and make it easy to follow.
  • However it should be difficult but possible to break free from these conventions if needed.
  • This is what an escape hatch is.
  • We essentially create higher friction for the developer to steer away from convention while allowing it to still be possible.

Single Source of Styles

  • We can use created classes to apply styles within other components like so:
const TextLink = styled.a`
    color: purple;
`;

const QuoteContent = styled.blockquote`
    margin: 0;
    background: black;

    ${TextLink} {
        color:blue;
    }
`;
  • Alternatively (even better) we can target the parent from the child.

    • This is referred to as Inversion of control nesting
    • Another way of wording it could be that "when I am in this location, style me this way"
    • Bonuses include:
      • All TextLink styles remain together
      • We aren't relying on our own memory to know where things are
    • Negatives:
      • we need to import the parent component
      • this can bloat bundles and hinder performance
    • Only use this for common situations, not one offs.
    const TextLink = styled.a`
      color: blue;
      text-decoration: none;
    
      ${QuoteContent} {
        color: black;
        text-decoration: revert;
      }
    `
    

Return