Skip to main content

Styling Components

danger

TODO: break out into multiple topics if necessary

Dynamic styling

We can dynamically apply styles to components using the className property:

Styles in CourseInput.css:

.form-control.invalid input {
border-color: red;
background: #ffd7d7;
}

.form-control.invalid label {
color: red;
}

Applying it inside the JSX through the className property:

const CourseInput = (props) => {
...

return (
<form onSubmit={formSubmitHandler}>
<div className={`form-control ${!isValid ? "invalid" : ""}`}>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);
};

Isolating styles to a component using Styled Components

The library Styled Components allows you to scope styles to a component. Instead of using a css file like this:

.button {
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;
}

.button:focus {
outline: none;
}

.button:hover,
.button:active {
background: #ac0e77;
border-color: #ac0e77;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.26);
}

You just write you CSS directly in the js file:

import styled from "styled-components";

// tagged template literal (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)
// for details on how this works see: https://styled-components.com/docs
const Button = styled.button`
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;

&:focus {
outline: none;
}

&:hover,
&:active {
background: #ac0e77;
border-color: #ac0e77;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.26);
}
`;

// old component syntax
// const Button = props => {
// return (
// <button type={props.type} className="button" onClick={props.onClick}>
// {props.children}
// </button>
// );
// };

export default Button;

Using dynamic styles

We can also dynamically apply styles based upon props on the elements with the "Styled Components" library. This is done like this:

const FormControl = styled.div`
margin: 0.5rem 0;

& label {
font-weight: bold;
display: block;
margin-bottom: 0.5rem;
color: ${(props) => (props.invalid ? "red" : "black")};
}

& input {
display: block;
width: 100%;
border: 1px solid ${(props) => (props.invalid ? "red" : "#ccc")};
background: ${(props) => (props.invalid ? "#ffd7d7" : "transparent")};
font: inherit;
line-height: 1.5rem;
padding: 0 0.25rem;
}

& input:focus {
outline: none;
background: #fad0ec;
border-color: #8b005d;
}

// We don't need these anymore since we dynamically apply the styles above
// &.invalid input {
// border-color: red;
// background: #ffd7d7;
// }

// &.invalid label {
// color: red;
// }
`;

// here we set the invalid prop
const CourseInput = (props) => {
...
return (
<form onSubmit={formSubmitHandler}>
<FormControl invalid={!isValid}>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</FormControl>
<Button type="submit">Add Goal</Button>
</form>
);
};

Isolating styles to a component using CSS Modules

Another way to scope styles to a component is to use CSS Modules. In this approach we keep our CSS in a separate file and import it into our JSX file in a special way:

// We name our file with the .module extension
import styles from "./Button.module.css";

// then we can use the style in our component
const Button = (props) => {
return (
<button type={props.type} className={styles.button} onClick={props.onClick}>
{props.children}
</button>
);
};

In the DOM the component now looks like this:

<button type="submit" class="Button_button__plK1F">Add Goal</button>

The style is applied through the class Button_button__plK1F. The naming consists of <component-name>_<style-name>__<unique-hash>.

If you have styles in your CSS with names like "form-control" where a dash is in the name, you need to access them like this: styles["form-control"].

Adding a conditional style can be done like this:

return (
<form onSubmit={formSubmitHandler}>
<div
className={`${styles["form-control"]} ${!isValid ? styles.invalid : ""}`}
>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);