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
andfigcaption
) 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 thecontent
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
- All
- 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; } `