본문 바로가기
카테고리 없음

[React/UI] 최대한 css로 로그인 폼 만들기

by sweesweet 2025. 2. 20.

 

예전에는 저렇게 focus됐을 때 색이 변하는 걸 css로 한 적이 없다. 보통 input을 div로 감싸서 처리해서 거의 다 useState로 처리 했었는데, 이번에 만들어보면서 css 가상클래스를 몇개 배울 수 있어서 좋았다.

 

1.이번에 사용한 css 가상 클래스

:focus - 해당 요소가 focus(클릭, 입력 등) 됐을 때
:focus-within - 해당요소 혹은 포커스된 요소를 포함하는 요소
:placeholder-shown - placeholder가 보여졌을 때(어떤 입력도 하지 않았을 때)
:not(선택자) - 부정(단일선택자만 가능)

 

원래는 :has()를 사용해 만드려 했는데, 2023년 말에 도입되기 시작한거라 오래된 브라우저에 작동하지 않아 제외했다.

2.input 컴포넌트와 css

컴포넌트

const Input = ({ children, ...props }) => {
  return (
    <div className="input__wrapper">
      <input {...props} className="input__field " />
      <label htmlFor={props.id} className={`input__label ${props.type}`}>
        {props["data-label"]}
      </label>
      {children}
    </div>
  );
};
export default Input;

 

label이 input 뒤에있는 이유는 css 결합자로는 앞에 있는 걸 처리 하는게 어렵기 때문에 뒤로 넣는게 맞을 것 같다.

input의 속성을 다 넣기 위해 {...props} 사용

더보기

:has()를 사용하지 않기로 하면서 이전과 html구조가 살짝 바뀌었다.

그로 인해서 오류와 만나게 되었는데, input요소는 enclosing tag기 때문에 비어있어야한다는 오류였다.

이 오류는 객체분해를 사용해 children을 맨 앞으로 뺴고 뒤에다가 스프레드 연산자를 사용해 (...props)해서 해결할 수 있었다.  

Input 컴포넌트가 모든 타입의 input 요소를 처리하는게 아니기 때문에, 클래스를 다르게 주던가 해야 할듯?

 

css

.input__wrapper {
  display: flex;
  position: relative;
  padding: 0.5rem 0;
  border: 0.063rem solid gray;
  border-radius: 0 0.5rem 0 0.5rem;
  margin: 1rem 0.5rem;
  box-shadow: 0.063rem 0.063rem 0.625rem rgba(255, 255, 255, 0.2);
  align-items: center;
}
.input__wrapper:focus-within {
  border-color: var(--primary);
}
.input__field {
  border: 0;
  outline: 0;
  color: var(--text);
  padding: 0.5rem;
  padding-right: 2rem;
  background: transparent;
  transition: border-color 0.2s;
  border: 0.125rem;
}
.input__field::placeholder {
  visibility: hidden;
}
.input__field:placeholder-shown + .input__label {
  font-size: 0.875rem;
  cursor: text;
  color: var(--secondary);
}
.input__label {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  transform: translate(0, 50%);
  color: var(--secondary);
  padding: 0 0.3rem;
}
.input__label,
.input__field:focus + .input__label {
  font-size: 0.75rem;
  transition: 0.3s;
}
.input__field:not(:focus):not(:placeholder-shown) + .input__label,
.input__field:focus + .input__label {
  background-color: var(--background);
  transform: translate(0.2rem, -0.75rem);
}
.input__field:focus + .input__label {
  color: var(--primary);
}

 

 input과 label을 감싸고 있는 .input__wrapper에서 나머지는 꾸미는 용도고 레이아웃이나 그런걸 위해선 아래와 같은 부분이 필수적으로 필요하다.기능이 있는 아이콘을 배치할 필요가 없다면 굳이 display:flex;는 하지 않아도 될 듯 하다.

.input__wrapper{  
  display: flex;
  position: relative;
  align-items: center;
  }

 

:focus-within은 input이 focus될 때 부모 요소의 스타일을 바꿀 때 아주 유용하다.

div.input__wrapper에 감싸져 있는 하위 요소인 input이 focus 됐을 때 input요소의 outline의 색상이 변경되는 것 처럼  div요소의 border 색상을 변경 할 수 있다.

.input__wrapper:focus-within {
  border-color: var(--primary);
}

 

프리뷰에서 봤듯이 포커스 될 때 보여지는 효과를 주기 위해서 기존의 placeholder를 가리고, label이 아무 것도 입력하고 있지 않았을 때는 placeholder와 같이 보여진다. 

이때 label을 나눠서 생각을 해야하는데

input에 아무것도 입력하지 않았을 때/ 그냥 label/ input에 포커스 됐을 때/ input에 포커스도 안됐고 무언가 입력이 되어있을 때

이렇게 나눠서 생각하면 된다.

/* placeholder가리기 */
.input__field::placeholder {
  visibility: hidden;
}
/* input에 아무것도 입력하지 않았을 때의 label*/
.input__field:placeholder-shown + .input__label {
  font-size: 0.875rem;
  cursor: text;
  color: var(--secondary);
}
/* 라벨의 기본 위치는 인풋 안*/
.input__label {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  transform: translate(0, 50%);
  color: var(--secondary);
  padding: 0 0.3rem;

}
.input__label,
.input__field:focus + .input__label {
  font-size: 0.75rem;
  transition: 0.3s;
}
/*input에 무언가 입력되어있고, 포커스는 안되어있는 상태일 때의 label,
input이 포커스 되어있을 때의 label*/
.input__field:not(:focus):not(:placeholder-shown) + .input__label,
.input__field:focus + .input__label {
  background-color: var(--background);
  transform: translate(0.2rem, -0.75rem);
}
/*input이 포커스 되어있을 때의 label*/
.input__field:focus + .input__label {
  color: var(--primary);
}

 

 


참고자료

https://x.com/davidm_ml/status/1882516551838093623

https://developer.mozilla.org/ko/docs/Web/CSS/:focus-within

https://developer.mozilla.org/ko/docs/Web/CSS/:not

https://developer.mozilla.org/ko/docs/Web/CSS/:placeholder-shown