Reputation: 61
I have just recently switched to using CSS Modules in my project and have come into a bit of an issue.
In the simple code example below, I am creating an input component that I want to be able to reuse as a form field when creating forms. The issue comes in when I want to be able to style the input component differently in certain situations.
FormInput.js
const FormInput = props => (
<FormControl>
<InputLabel>{props.label}</InputLabel>
<Input />
</FormControl>
);
Form.js
import React from 'react';
import Button from '@material-ui/core/Button';
import Input from './input';
const Form = () => (
<form>
<Input label="Name" />
<Button> Submit </Button>
</form>
);
I know this can be done using styled-components, but I am really looking for a solution that uses CSS Modules. Any help would be appreciated
Upvotes: 1
Views: 4481
Reputation: 19772
You can simply import the css and pass it as a prop
to Input
. In this case, you can pass it down as className
.
Note: As you'll notice below, CSS
can be a bit redundant when it comes to styling nested and pseudo elements, which is why I'd highly recommend SASS (scss
or less
) for preprocessed stylesheets (post-processing will convert the SASS
stylesheets to a plain CSS
stylesheet for you).
Working example (reusing Input
but styling it with different classes
):
components/Input (it accepts a className
string, onChange
function (required), label
string, name
string (required), and a value
string)
import React from "react";
import PropTypes from "prop-types";
const Input = ({ className, onChange, label, name, value }) => (
<div className={className}>
<label htmlFor={name}>{label}: </label>
<input value={value} onChange={onChange} name={name} type="text" />
</div>
);
// PropTypes ensures that passed down props adhere to the type checking
// rules defined below
Input.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func.isRequired,
label: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.string
};
export default Input;
styles.css (you'll need to use camelCase
instead of snake-case
for className
s)
.appContainer {
text-align: center;
padding: 20px;
}
input {
height: 40px;
vertical-align: middle;
display: inline-block;
border: 0 none;
padding: 0 10px;
background: #fff;
color: #666;
border: 1px solid #e5e5e5;
transition: 0.2s ease-in-out;
transition-property: color, background-color, border;
font-size: 15px;
}
.nameField {
font-weight: bold;
color: blue;
margin-bottom: 20px;
}
.nameField > input {
color: green;
}
.emailField {
font-weight: bold;
color: red;
margin-bottom: 20px;
}
.emailField > input {
color: blue;
}
.resetButton {
cursor: pointer;
background-color: transparent;
color: #222;
border: 1px solid #e5e5e5;
margin: 0;
overflow: visible;
box-sizing: border-box;
padding: 0 30px;
vertical-align: middle;
font-size: 14px;
line-height: 38px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
transition: 0.1s ease-in-out;
transition-property: color, background-color, border-color;
}
.resetButton:hover {
background-color: transparent;
color: #222;
border-color: #b2b2b2;
}
.resetButton:focus {
outline: none;
}
components/App (import
all of the css
as classes
and apply them as needed -- you can also use ES6 destructuring to pull out single classes, for example:
import { appContainer } from "./styles.css";
)
import React from "react";
import { render } from "react-dom";
import Input from "./components/Input";
import useFieldHandler from "./hooks/useFieldHandler";
import classes from "./styles.css";
const App = () => {
const { values, handleChange, resetValues } = useFieldHandler({
name: "",
email: ""
});
return (
<div className={classes.appContainer}>
<h1>CSS Modules</h1>
<Input
label="Name"
name="name"
className={classes.nameField}
value={values.name}
onChange={handleChange}
/>
<Input
label="Email"
name="email"
className={classes.emailField}
value={values.email}
onChange={handleChange}
/>
<div className={classes.btnContainer}>
<button
type="button"
className={classes.resetButton}
onClick={resetValues}
>
Reset
</button>
</div>
</div>
);
};
render(<App />, document.getElementById("root"));
Upvotes: 1
Reputation: 497
So what I was explaining above is to pass a unique id as a prop and then you need to use a template literal to append that to the existing class name in the Input component.
FormInput.js
const FormInput = props => (
<FormControl>
<InputLabel>{props.label}</InputLabel>
<Input uniqueClass='unique-class'/>
</FormControl>
);
Form.js
import React from 'react';
import Button from '@material-ui/core/Button';
import Input from './input';
const Form = () => (
<form>
<Input uniqueClass='unique-class' label="Name" />
<Button> Submit </Button>
</form>
);
Upvotes: 0