React - 컴포넌트 스타일링

gombibi·2023년 2월 19일
0

React

목록 보기
2/5

스타일링 방식
- CSS를 작성할 때 가장 중요한 점은 클래스명을 중복되지 않게 만드는 것!

1. 일반 CSS
1) 이름 짓는 규칙: 컴포넌트 이름 - 클래스 형태(App-headr) 혹은 BEM 네이밍(일종의 규칙을 준수하여 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성 ex) .card__title-primary)
2) CSS Selector: 특정 클래스 내부에 있는 경우에만 스타일 적용(ex) .App .logo: App안에 들어있는 .logo, .App a: App안에 들어있는 a태그 .생략)

2. Sass(Syntactically Awesome Style Sheets)
- CSS 전처리기; 복잡한 작업을 쉽게 할 수 있도록 해주고 스타일 코드의 재활용성을 높여주고 코드의 가독성을 높여 유지 보수를 쉽게 해줌.
-변수할당, 중첩구문, 모듈화, 믹스인(param받아서 속성 부여), 확장&상속, 연산자 기능 제공
.sass: 들여쓰기로 중첩 표현, 줄바꿈 만으로 속성 구분

body
	font: 100% $font-stack
    color: $primary-color

.scss: 중괄호로 중첩 표현, 세미콜론으로 속성 구분

body{
	font: 100% $font-stack;
    color: $primary-color;
}

-전세계적으로 사용자수나 활용이 SCSS > SASS
-SASS보다 SCSS가 기존 CSS와의 문법이 비슷, 호환성 좋음

3. CSS Module
-스타일을 작성할 때 CSS 클래스가 다른 CSS 클래스의 이름과 절대 충돌하지 않도록 파일마다 고유한 이름([파일이름]_[클래스 이름]_[해시값]) 형태을 자동으로 생성해줌. 클래스명 명명하느라 고민하지 않아도 됨.
-리액트 v2 버전 이상부터는 .module.css 확장자로 파일을 저장하기만 하면 적용됨
-글로벌 CSS라면 :global을 앞에 입력하여 명시
-classname 라이브러리: 여러 종류의 파라미터를 조합해 CSS 클래스 설정할 수 있어 컴포넌트에서 조건부로 클래스 설정하기 편함

//highlighted값이 true이면 클래스 적용, false이면 적용되지 않음
const MyComponent = ({highlighted, theme}) => (<div className={classNames('MyConponent', {highlighted}, theme)}>Hello</div>
);

-라이브러리 사용 없이 처리

const MyComponent = ({highlighted, theme}) => (<div className={`MyConponent ${theme} ${highlighted ? 'highlighted' : ''}`}>Hello</div>
);코드를 입력하세요

-CSS Module아닌 파일에서 CSS Module 사용하려면 :local 사용(ex) .local .wrapper{/*스타일*/})

4. Styled-component
-스타일을 JS 파일에 내장시키는 방식. (css-in-js 라이브러리의 일환; cf) emotion) 스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만들 수 있게 해줌.
-컴포넌트 상단에서 styled 를 import하고, styled.태그명을 사용하여 구현

*CSS Module과 Styled-component 비교
-CSS Module 적용

//Button.js
import React from 'react';
import styles from './Button.module.css';

const Button = (props) => {
	return (
		<button type={props.type} className={styles.button} onClick={props.onClick}>
			{props.children}
		</button>
	);
};
export default Button;
//Button.module.css
.button {
	width: 100%;
	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);
}

//반응형 디자인: 768px 미만 꽉채우기
@media (min-width: 768px) {
	.button {
		width: auto;
	}
}

-Styled-component 적용

//Button.js
import React from 'react';
import styled from 'styled-components';

//CSS-IN-JS
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;

	//반응형 디자인: 768px 이상 꽉채우기
    //실제로 사용한다면 아예 다른 파일로 모듈화 한 뒤 여기저기서 사용하는 방식이 편할 것(styled-components 매뉴얼에서 제공하는 유틸 함수 참고)
	@media (min-width: 768px) {
		width: 100%;
	}

	//&문자 사용하여 Sass처럼 자기 자신 선택 가능
	&:focus {
		outline: none;
	}

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

export default Button;

-조건부 스타일링

//CourseInput.js
import React, { useState } from 'react';
// import styled from 'styled-components';

import Button from '../../UI/Button/Button';
import styles from './CourseInput.module.css';

// 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 #ccc ${(props) => (props.invalid ? 'red' : '#ccc')};
// 		background: ${(props) => (props.invalid ? '#ffd7d7' : '#ccc')}
// 		font: inherit;
// 		line-height: 1.5rem;
// 		padding: 0 0.25rem;
// 	}

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

// 	// &.invalid input {
// 	// 	border-color: red;
// 	// 	background: #ffd7d7;
// 	// }

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

const CourseInput = (props) => {
	const [enteredValue, setEnteredValue] = useState('');
	const [isValid, setIsValid] = useState(true);

	const goalInputChangeHandler = (event) => {
		if (enteredValue.trim().length > 0) {
			setIsValid(true);
		}
		setEnteredValue(event.target.value);
	};

	const formSubmitHandler = (event) => {
		event.preventDefault();
		if (enteredValue.trim().length === 0) {
			setIsValid(false);
			return;
		}
		props.onAddGoal(enteredValue);
		setEnteredValue('');
	};

	return (
		<form onSubmit={formSubmitHandler}>
        	{/* 조건값을 참조하여 설정 */}
			{/* <FormControl className={!isValid && 'invalid'}> */}
			{/* <FormControl invalid={!isValid}> */}
            {/* 템플릿 리터럴 사용 */}
			<div className={`${styles['form-control']} ${!isValid && styles.invalid}`}>
				<label>Course Goal</label>
				<input type='text' value={enteredValue} onChange={goalInputChangeHandler} />
			</div>
			<Button type='submit'>Add Goal</Button>
		</form>
	);
};

export default CourseInput;
//CourseInput.module.css
.form-control {
	margin: 0.5rem 0;
}

.form-control label {
	font-weight: bold;
	display: block;
	margin-bottom: 0.5rem;
}

.form-control input {
	display: block;
	width: 100%;
	border: 1px solid #ccc;
	font: inherit;
	line-height: 1.5rem;
	padding: 0 0.25rem;
}

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

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

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

참고
-리액트다루는기술
-react complete guide

profile
말랑말랑한 개발자가 되자:D

0개의 댓글