새로 알게 된 CSS 개념 5가지
디자인 요구사항을 맞추려 노력하다가 새로 알게 된 CSS 개념 5가지 정리
:has()
:has()는 **”특정 자식 요소를 가지고 있는 부모 요소”**를 선택할 수 있는 가상 선택자입니다.
기존 CSS 선택자는 부모에서 자식 방향으로만 선택이 가능했는데요.
특정 부모 요소의 자식 요소를 선택하는 방법
선택자1 선택자2 { /* 선택자1 하위의 모든 선택자2에 적용될 스타일 */ } 선택자1 > 선택자2 { /* 선택자1 바로 하위 요소인 선택자2에 적용될 스타일 */ }
아래와 같이 작성하면 선택자2를 가지고 있는 선택자1에 스타일 코드가 적용됩니다.
선택자1:has(선택자2) {
/* 스타일 코드 */
}저는 이 has 선택자를 input 태그를 감싸고 있는 Wrapper 컴포넌트에 적용해서 input의 상태에 따라 border를 추가하는 기능에 활용했습니다.
저희 Input 컴포넌트의 일부 코드인데요. input 태그와 아이콘을 감싸고 있는 Wrapper 컴포넌트가 있는 구조입니다.
<S.Wrapper
$error={error}
$disabled={disabled}
className="input-wrapper"
>
<S.Input
autoComplete="off"
ref={ref}
disabled={disabled}
{...props}
/>
<S.IconWrapper>{icon}</S.IconWrapper>
</S.Wrapper>이 때 Wrapper 컴포넌트에 자식 input이 비어있을 때 (즉 placeholder가 보일 때) border를 추가하려면 has 선택자를 아래와 같이 활용하면 됩니다. (아래 코드 예시는 모두 styled-components 기준입니다.)
&:has(input:placeholder-shown) {
border: ${({ theme }) => `1px solid ${theme.color.semantic.border.default}`};
background: ${({ theme }) => theme.color.semantic.background.normal.default};
}간단하죠?

:placeholder-shown
위에서 사용한 :placeholder-shown도 이번에 새로 알게 된 가상 선택자 중 하나입니다.
이름에서도 알 수 있듯이 placeholder가 보이는 상태의 요소를 선택하는 선택자인데요.
이 선택자를 이용해서 JS 없이도 입력이 없는 경우와 있는 경우를 다르게 스타일링 할 수 있습니다!
빈 상태인 경우에는 placeholder가 보일 테니 input:placeholder-shown 로 선택해 주면 되고, 입력이 있는 경우에는 placeholder가 보이지 않을테니 input:not(:placeholder-shown) 이렇게 not 연산자로 반대의 경우를 선택해주면 됩니다.
/* 입력값이 있는 경우 (placeholder가 보이지 않는 경우) */
&:has(input:not(:placeholder-shown)) {
border: ${({ theme }) => `1px solid ${theme.color.semantic.border.strong}`};
background: ${({ theme }) => theme.color.semantic.background.normal.default};
}
/* 입력값이 없는 경우 (placeholder가 보이는 경우) */
&:has(input:placeholder-shown) {
border: ${({ theme }) => `1px solid ${theme.color.semantic.border.default}`};
background: ${({ theme }) => theme.color.semantic.background.normal.default};
}placeholder를 기준으로 빈 값 여부를 확인하는 것이기 때문에 placeholder가 있는 Input에서만 사용할 수 있다는 점만 유의한다면 정말 간단하게 빈 값을 판단할 수 있는 방법이라고 생각합니다.
:focus-within
:focus 선택자는 많이들 써 보셨을 것 같습니다. :focus는 이름 그대로 클릭되는 등의 이벤트로 포커스 된 상태의 요소를 선택하는 가상 선택자인데요.
:focus-within은 자식 요소로 포커스된 요소를 가지고 있는 부모 요소를 선택할 수 있는 가상 선택자입니다.
저는 :focus-within 선택자를 :has를 사용했던 Input 컴포넌트의 요구사항을 위해서 사용했는데요. input 태그가 포커스 된 경우에 input 태그를 감싸고 있는 Wrapper 컴포넌트에 border를 추가하는 코드는 아래와 같습니다.
&:focus-within {
border: ${({ theme }) => `2px solid ${theme.color.semantic.orange.default}`};
background: ${({ theme }) => `${theme.color.semantic.background.normal.default}`};
}
min-width: 0
flex box 안에 컴포넌트를 배치할 때 어떻게 해도 자식 요소가 부모 요소의 크기를 넘어버리는 상황이 생기면 스트레스를 많이 받는데요… (경험담입니다…) 놀랍게도 자식 요소에 min-width: 0을 지정하는 것으로 문제를 간단히 해결할 수 있었습니다.
flexbox에서 가변요소의 자식width가 부모width를 넘을때
flex box 안의 자식 요소를 flex item이라고 하고, flex item의 기본 속성은 min-width: auto 입니다. 이 설정은 내부 컨텐츠의 크기에 따라서 최소 너비를 조정하는 것이기 때문에 자식 요소의 크기가 커져버리면 어떻게 해도 부모 요소의 크기를 벗어나게 되는 것이죠. 그래서 min-width: 0 을 추가해서 min-width: auto 을 상쇄시켜 주면 문제가 해결됩니다. 👏
clamp
clamp는 반응형 디자인을 적용할 때 도움을 많이 받은 함수입니다. 구조는 아래와 같이 생겼는데요.
clamp(min, val, max)
/* min: 최소값 */
/* val: 가변값 */
/* max: 최댓값 */clamp 함수로 반환되는 값은 기본적으로 val로 전달한 가변값이 됩니다. 가변값이라 하면 vw, em, rem 같은 상대 단위 값을 말해요.
하지만 val이 min에 지정한 값보다 작다면 clamp 함수는 min을 반환하고, 비슷하게 val이 max에 지정한 값보다 커지면 max를 반환합니다.
저는 요소 사이 간격을 지정할 때 clamp를 사용했는데, 자료를 찾다 보니 폰트 사이즈를 반응형으로 지정할 때에도 clamp를 유용하게 사용할 수 있다는 것을 알게 되었습니다!