Profile Image
블로그를 옮겼어요.
미뤄두었던 CSS를 공부하자.
개발
2024.02.04.

개발을 할 때 시간이 오래걸리는 내 모습을 보면서 ‘어떤 부분이 부족한 걸까’ 고민하기 시작했다.
모든 부분에서 한 없이 부족하지만, 가장 부족한건 TypescriptCSS였다.
이번에 사이드프로젝트를 진행하며, CSS에 대해 복습하고 새롭게 내용을 정리해보자

1. aspect-radio

  • 원본 이미지 비율을 유지하면서, 크기를 조절하고 싶을 때 aspect-ratio를 사용하면 된다.
  • 특히 모바일 뷰를 개발할 때 모바일의 크기가 모두 제각각이다.
    이를 대응하려면 aspect-radio를 사용하는 것이 좋다.
// 실제 이미지를 모달창으로 구현할 때 모바일 width / height를 고려해서 aspect-ratio를 사용했다.
.letter-bg {
  width: 100%;
  height: auto;
  aspect-ratio: 598 / 708;
  background-color: white;
  background-image: url('/images/letter-bg.png');
  background-repeat: no-repeat;
  background-size: contain;
  background-position: center bottom;
}

1-1. 예외상황

  • aspect-radio가 적용되지 않는 3가지 예외상황이 있다.
  • 특히, CSS는 예외상황을 알지 못할 경우에도 에러로 알려주지 않으니 주의해야한다.
  1. width, height가 고정으로 지정되었을 경우.
  2. 마찬가지로 min-height, min-width와 같이 고정으로 지정되었을 경우.
  3. 내용이 element의 크기를 넘어갈 경우.

2. inline-block, inline, inline-flex

진짜진짜 매번 헷갈린다. inline-flex는 사실 처음 알았다;
다른 요소는 이전에 정리해놓은 내용이 있다. 인용해서 다시 복습해보자


2-1. inline

참고로 inline은 in the same line이라고 이해했다.
즉, 같은 줄에 위치할 수 있다.

 <div>나는 Div-1이다</div>
 <span>나는 span-1이다.</span>
 <span>나는 span-2이다.</span>
 <span>나는 span-3이다.</span>
 <div>나는 Div-2이다</div>
div {
  background: yellow;
  width: 200px;
  height: 50px;
  margin: 20px;
  padding: 10px;
}

span {
  background: red;
  width: 100px;
  height: 20px;
  margin: 10px;
  padding: 5px;
}

구분이 명확하지 않아, 키워드별 경계선을 추가했습니다. 🙇‍♂️


  • 하나의 요소(inline) 옆에 다른 요소(inline)이 올 수 있음
    block인 div와 다르게 inline인 span은 옆에 다른 요소가 올 수 있다.
    block인 div와 다르게 inline인 span은 옆에 다른 요소가 올 수 있다.

  • inline는 너비와 높이가 없음

    inline은 width와 height값을 지정하면 다음과 같이 warning이 뜬다.
    inline은 width와 height값을 지정하면 다음과 같이 warning이 뜬다.

    • background-color하면 색이 적용되지 않음.
    • 하지만 문자, 또는 어떤 내용이 들어 있을 경우 색이 적용된다.

  • inline는 margin과 padding을 줄 수 있음

    • span(inline)는 margin을 사방으로 주더라도 좌/우만 적용되는 것을 확인가능
      (명확히 하기 위해 padding은 잠깐 주석처리했었음)
      margin을 상하좌우로 10px씩 주었지만 좌우만 적용 됨
      margin을 상하좌우로 10px씩 주었지만 좌우만 적용 됨
    • padding은 전 방향 모두 줄 수 있음
      padding을 상하좌우로 5px씩 주었을 때, 모두 적용 됨
      padding을 상하좌우로 5px씩 주었을 때, 모두 적용 됨

      단, 이 블로그에서 제시하듯이, inline은 height값이 없기 때문에, position이 block인 element와 수직으로 놓을 경우, 겹치는 부분이 생긴다. 이를 해결하기 위해선 inline-block을 사용해야 한다.
  • inline의 종류는 span, a, img가 있음 (나머진 대부분 Block)



2-2. inline-block

  • inline-block은 inline과 block의 특징을 모두 가지고 있음
    • 즉, inline처럼 같은 줄에 위치할 수 있고, block처럼 너비와 높이를 가질 수 있음
// 위에 제시했던 코드에서 다음의 CSS만 변경햇음
div {
  display: inline-block; // inline-block으로 변경
  background: yellow;
  width: 200px;
  height: 50px;
  margin: 20px;
  padding: 10px;
}

span {
  display: inline-block; // inline-block으로 변경
  background: red;
  width: 100px;
  height: 20px;
  margin: 10px;
  padding: 5px;
}
  • 위에서 언급했듯이, span은 inline이 default이고, div는 block가 default이다. 이를 inline-block로 변경했다.

변경 후, 이와 같은 이미지를 띄는데, 하나씩 살펴보자
변경 후, 이와 같은 이미지를 띄는데, 하나씩 살펴보자

  1. 먼저 위 이미지를 보고, block은 inline-block으로 변경되면서 inline처럼 같은 줄에 위치할 수 있게 되었다는 것을 알 수 있다.
  2. 그리고 inline이었던 span은 너비와 높이를 가질 수 있게 되었다는 것을 알 수 있다.
  3. 즉 내부는 block 속성을, 외부는 inline 속성을 가지게 된 하이브리드 형태이다.
  4. inline-block는 대표적으로 button, input, select가 있다.

2-3. inline-flex

update: 2025-03-15
inline-flex를 재정리한 글을 업데이트 했습니다.

  • 다음과 같은 코드가 있다.
<div class="flex">
    <div class="div-box">1</div>
    <div class="div-box">2</div>
    <div class="div-box">3</div>
</div>
<div class="flex">
    <div class="div-box">4</div>
    <div class="div-box">5</div>
    <div class="div-box">6</div>
</div>
.flex {
  display: flex;
}

.div-box {
  width: 100px;
  height: 100px;
  background-color: white;
  border: 1px solid black;
  border-radius: 5px;
  margin: 10px;

  display: flex;
  justify-content: center;
  align-items: center;
}
  • 위 코드는 다음과 같은 이미지로 나타난다.

class가 flex인 컨테이너는 수직으로 쌓인다.
class가 flex인 컨테이너는 수직으로 쌓인다.


.flex {
  display: inline-flex; // inline-flex로 변경
}

.div-box {
  width: 100px;
  height: 100px;
  background-color: white;
  border: 1px solid black;
  border-radius: 5px;
  margin: 10px;

  display: flex;
  justify-content: center;
  align-items: center;
}
  • flex에서 inline-flex로 변경해주었다. 그 결과는 다음과 같다.

class가 flex인 컨테이너는 수평이 되었다.
class가 flex인 컨테이너는 수평이 되었다.

  • 즉 이를 정리해보면 다음과 같다.

flex inline flex

  • flex 컨테이너가 inline-flex로 변경되면, 수직으로 쌓이던 flex-item들이 수평으로 쌓이게 된다.

3. relative와 absolute

매번 헷갈리는 absolute와 relative, 더 나아가 position..

3-1. position: relative

<body>
  <div>
    <div class="green"></div>
  </div>
</body>
div {
  width: 300px;
  height: 300px;
  color: white;
  background-color: wheat;
}

.green {
  background-color: teal;
  width: 100px;
  height: 100px;
}

relative 속성을 주기 전
relative 속성을 주기 전


.green {
  background-color: teal;
  width: 100px;
  height: 100px;
  position: relative; // relative를 주었을 때
  right: 20px; // right를 주었을 때
  left: 30px; // left를 주었을 때
}
  • 모두 동일하게 유지한 상태에서 position | right | left를 주었을 때 다음과 같은 결과를 보인다.

relative 속성을 주고 난 뒤
relative 속성을 주고 난 뒤


3-2. position: absolute

.green {
  background-color: teal;
  width: 100px;
  height: 100px;
  position: absolute; // absolute를 주었을 때
  right: 0px; // left를 주었을 때
}
  • relative와 모든 게 동일한 환경에서 position과 right만 변경해주었다.

부모인 div를 벗어나, 화면에서 가장 오른쪽으로 이동했다.
부모인 div를 벗어나, 화면에서 가장 오른쪽으로 이동했다.

  • 현재의 absolute는 부모인 div를 벗어나, body를 기준으로 움직인다.
  • default인 body를 div로 변경해보자.
div {
  width: 300px;
  height: 300px;
  color: white;
  background-color: wheat;
  position: relative;  // div에 relative를 주었다.
}

.green {
  background-color: teal;
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0px;
}

부모인 div를 기준으로 가장 오른쪽으로 이동했다.
부모인 div를 기준으로 가장 오른쪽으로 이동했다.

  • 이렇게 이동할 수 있는 기준이 무엇일까? 다음을 확인해보자
<div>
  <div class="sibling"></div>
  <div class="green"></div>
</div>
div {
  width: 300px;
  height: 300px;
  color: white;
  background-color: wheat;
}

.sibling {
  width: 100px;
  height: 100px;
  background-color: red;
  position: relative; // sibling에 relative를 주었다.
}

.green {
  background-color: teal;
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0px;
}
  • class가 green인 div박스에 형제 div인 sibling class를 추가해주었다.
  • 그리고 sibling에 css로 position relative를 추가해주었다.

green class를 줬던 div박스는 다시 부모를 벗어나, 화면에서 가장 오른쪽으로 이동했다.
green class를 줬던 div박스는 다시 부모를 벗어나, 화면에서 가장 오른쪽으로 이동했다.

  • 즉, 부모 요소 중에서 position 속성이 relative인 요소가 있다면
    이 부모를 기준으로, absolute를 줬던 자식요소를 자유롭게 배치할 수 있다.

4. user-select, pointer-events

4-1. user-select

강아지 이미지를 드래그하면 이미지가 선택되어버린다.
강아지 이미지를 드래그하면 이미지가 선택되어버린다.

  • 위와 같은 상황이라고 가정해보자
  • 이럴 경우 이미지나 요소들이 선택되지 않도록 막아주는 방법은 없을까..?

모바일에서 웹뷰로 제작할 시, 유저가 이미지를 드래그하면 이미지가 선택되어버린다.

  • 이를 해결하기 위해선 다음과 같은 방법이 있다.
// 위 이미지는 다음과 같은 HTML로 구성되어 있다.
<section>
  <div
    style="
      //...
      user-select: none;   /* user-select: none을 주었다. */
    "
  >
    <img style="//..." src="./images.png" />
    <input
      style="
        //...
      "
      readonly
      value="귀여운 강아지"
    />
  </div>
</section>
  • 이렇게 하면 더 이상 이미지가 선택되지 않는다.
  • user-select 속성은 유저가 텍스트를 선택할 수 있는지를 지정해주는 속성이다.
  • 위와 같이 div에 user-select: none을 주면, div 내부의 모든 텍스트가 선택되지 않는다.
  • 하지만 input의 readOnly로 준 value는 여전히 선택이 가능하다.

여전히 input의 value는 선택가능하다.
여전히 input의 value는 선택가능하다.


4-2. pointer-events

  • 위와 같은 현상을 막아주기 위해, pointer-events를 사용할 수 있다.
// 위와 같은 HTML로 구성되어 있다.
<input
  style="
    //...
    pointer-events: none; /* pointer-events: none을 주었다. */
  "
  readonly
  value="귀여운 강아지"
/>
  • pointer-events는 사실 클릭 이벤트를 막아주는 속성이다. 즉, 위와 같이 선택을 막아주는 요소는 아니다.
  • 그래서, 만약 readOnly가 아니라면, 사용해선 안된다. input 박스를 클릭했는데, value를 변경할 수 없게 되니 말이다.

나의 경우엔, input / textarea가 readOnly인 경우에만 pointer-events를 사용했다.


5. 같은 형제 구조에서 absolute를 강제할 수 있는 방법은 없을까?

absolute를 형제 구조를 기준으로 위치를 다시 잡을 순 없을까?

  • 다음과 같은 코드가 있다.
  • 요구사항은 다음과 같았다.
    1. 자식 1의 북동쪽 꼭지점을 기준으로 5px 위에 자식 2를 위치시킨다.

이런 구조를 가지길 바랐다.
이런 구조를 가지길 바랐다.

  • 아래와 같이 간략한 코드를 구성해봤다.
  <section id="modal" class="layout">
    <div id="swiper" class="parent">
      <div id="contents" class="child-1">자식 1</div>
      <div id="close-button" class="child-2">x</div>
    </div>
  </section>
  • 여기서 이제 close-button을 어떻게 contents의 위에 놓을 수 있을까?

5-1. layer 층을 하나 더 두기

  • 가장 먼저 생각난 건, relative와 absolute를 사용하는 것인데, 이를 위해 layer층을 하나 더 두는 것이다.
  <section id="modal" class="layout">
    <div id="swiper" class="parent">
      <div id="add-layer" class="add-layer">  <!-- layer를 추가햇다. -->
        <div id="contents" class="child-1">자식 1</div>
        <div id="close-button" class="child-2">x</div>
      </div>
    </div>
  </section>
//..

.add-layer {
  position: relative;
}

.child-1 {
  //...
  width: 150px;
  height: 150px;
}

.child-2 {
  // ...
  width: 20px;
  height: 20px;
  position: absolute;
  right: 0;
  bottom: calc(150px + 20px + 5px);
}
  • 결국 add-layer를 기준으로 두기 위해 relative를 주고, child-2에 absolute를 주었다.
  • 그리고 오른쪽 가장 끝에 위치시키기 위해 right: 0을 주었다.
  • 마지막으로 bottom을 주었는데, 이는 child-1의 높이 + child-2의 높이 + 5px를 주었다.

완성은 했는데, 다른 방법은 없을까?
완성은 했는데, 다른 방법은 없을까?


완성은 했지만, 꼭 layer를 하나 더 추가해줘야하는걸까.. 다른 방법은 없을까?
그렇다고, Javascript를 쓰고 싶지도 않았다.
개인적으로 스타일은 최대한 CSS로만 처리하고 싶다.


5-2. anchor를 사용하기

anchor는 현재 정식 반영된 기능이 아니라, 실험적인 기능이다.

먼저 chrome에서 chrome://flags로 들어가서 #enable-experimental-web-platform-features를 활성화해주자.
💬 크롬을 열고 chrome://flags/#enable-experimental-web-platform-features를 url에 붙여넣으면 됩니다!


그 후엔 아래와 같이 polyfill과 class를 추가해주자.

  <head>
    //...
    <!-- polyfill을 추가해주었다. -->
    <!-- https://github.com/oddbird/css-anchor-positioning?tab=readme-ov-file -->
    <script type="module">
      if (!("anchorName" in document.documentElement.style)) {
        import("https://unpkg.com/@oddbird/css-anchor-positioning");
      }
    </script>
  </head>
  <body>
    <section id="modal" class="layout">
      <div id="swiper" class="parent">
        <div id="contents" class="child-1 anchor">자식 1</div> <!--class에 anchor를 추가했다. -->
        <div anchor="my-anchor" id="close-button" class="child-2 mover">x</div> <!--  anchor="my-anchor"와 class에 boat를 추가했다. -->
      </div>
    </section>
  </body>
  //...
    .child-1 {
      //...
    }

    .child-2 {
      //...
    }

    .anchor {
      anchor-name: --my-anchor;
    }

    .mover {
      anchor-default: --my-anchor;
      bottom: calc(anchor(top) + 5px);
      left: calc(anchor(right) - 20px);
    }
  • 기존의 absolute, relative는 모두 제거했다!
  • 그리고 anchor를 사용하기 위해선, anchor-name을 사용해야한다.
  • 이제 자식 1을 기준으로, 자식 2를 움직이기 위해 class mover를 추가했다.
    • 이렇게 했을 때, anchor-default(자식1)을 기준으로 자식2를 움직일 수 있다.

동일하게 적용할 수 있다.
동일하게 적용할 수 있다.


정리해보면, anchor를 이용하면, javascript를 사용하지 않아도 되고, 래퍼요소를 추가할 필요도 없다.
얼른.. 정식으로 반영되었으면 좋겠다. 🥲


참고자료

© 2025 Geuni, Powered By Gatsby.