코로 넘어져도 헤딩만 하면 그만
[CSS, JS] 미디어 쿼리 적용해서 반응형 사이즈 맞추기 본문
초창기에는 통용되는 기기들의 넓이가 몇 개로 규격화 되어 있었지만, 지금은 수많은 액정과 모니터가 쏟아져 나오고 있기 때문에 일일이 지정하기가 불가하다. 따라서 '반응형 웹'을 만들 필요성 또한 커졌다.
화면의 크기에 따라 내부 구성요소가 변화하는 반응형 웹은 미디어 쿼리를 적용하여 만들 수 있다.
@media media-type and (media-feature-rule) {
/* CSS rules go here */
}
이것이 가장 기초적인 미디어 쿼리의 구조다.
'media-type'에는 all, print, tv 등의 '기기 타입'을 지정할 수 있는데 보통 all로 두는 듯 하다. and는 영어로 써야 하고 그 뒤의 ()에 max-width: 1280px 같이 원하는 조건을 삽입한다. min-width와 max-width는 얼마 이상과 이하로 해석할 수 있다. 즉, @media all and (max-width: 1280px) {color: blue}는 화면 너비 1280px 이하에서 푸른 색 폰트를 적용하라는 뜻이다. 이처럼 이미 완성되어 있는 웹페이지를 미디어쿼리를 통해 조금씩 css를 수정해서 여러 너비에 맞게 바꿀 수 있다.
max나 min이 없이 기본적인 width만 쓸 수도 있지만, 반응형 웹에서는 이상과 이하로 구분하는 것이 훨씬 유용하므로 거의 쓰이지 않는다. 미디어 쿼리는 css를 통해 제어하는 것이기에 css/scss 파일 한 개 맨 하단에 @media~~ 처럼 삽입해주는 게 관례다. 물론 여러 개를 삽입할 수도 있다.(반응형 만들 땐 오히려 권장된다.)
보통의 웹사이트는 반응형으로 수정할 때 최소 3개의 미디어쿼리 레이아웃 변동 기준을 가진다.
예시로 만든 L모 재단의 헤더다. 다음과 같이 변동한다.
1. container(요소들이 다 들어가 있는 규격틀)의 너비
@media all and (max-width: 1860px) {
#header {
.container {
width: 100%;
padding: 0 50px;
box-sizing: border-box;
}
}
}
당연히 기존 레이아웃에 비례하여 1860보다 더 작을 수도 있다. 1580이나 1400도 가능. 주로 내부 요소들이 컨테이너 사이즈보다 좁아졌을 때 좌우 패딩을 가져 예쁘게 정렬되도록 한다.
2. 1280쯤 갔을 때 -메뉴와 로고가 겹쳐지기 전의 너비
창의 좌우가 줄어들다보면 맨 위 네비게이션 항목들이 보기 싫게 중첩되려는 순간이 있다. 바로 이때를 두번째 분기점으로 둔다. 네비게이션을 지워 우측의 메뉴-아이콘을 누르면 나오도록 정돈한다. 아래 코드에서 li:last-child를 block로 해둔 건 기존 웹페이지의 마지막에 둔 메뉴 아이콘을 hidden 처리 해뒀기 때문이다.
너비가 1280이하로 줄어들 때에만 우측에 아이콘이 나타나 사라진 메뉴가 전부 그쪽으로 이동하도록 해두었다.
@media all and (max-width: 1280px) {
// 여기다가 1280보다 작아졌을때 어떤 css 쓸건지 적용
header {
height: 100px;
}
header .util li:last-child {
display: block;
}
#gnb {
position: fixed;
height: 100vh;
width: 100%;
left: 100%;
top: 100px;
background-color: #fff;
transition: all 0.25s ease;
&.on {
left: 0;
}
.list {
display: block;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
width: 100%;
> li {
border-bottom: 1px solid #ccc;
.depth01 {
justify-content: start;
}
.depth02 {
background-color: #eee;
position: static;
padding: 0;
border-radius: 0;
&:before {
display: none;
}
li {
margin-top: 0;
border-top: 1px solid #ccc;
a {
display: flex;
padding: 15px 40px;
text-align: left;
align-items: center;
&:before {
content: "";
display: block;
width: 3px;
height: 3px;
background-color: $main-color;
margin-right: 10px;
border-radius: 100px;
}
}
}
}
}
}
}
}
장황해보이지만, 사실상 미디어쿼리 기능 자체와는 별 관련 없는 부분이다. mobile 웹을 만들 때 적용하는 대로 메뉴를 다듬어준 것이다. left:0; 으로 보이게 두고 작업하다가 마지막에 다 만든 항목을 left:100%;로 숨기면 된다. 자바 스크립트로 사용자가 메뉴 아이콘을 click해서 "on" class가 붙을 때, left:0;이 되며 메뉴창이 나타나게 한다.
3. 640px보다 작을 때-모바일 최소의 너비
@media all and (max-width: 640px) {
// 여기다가 640px보다 작아졌을때 어떤 css 쓸건지 적용
header {
height: 60px;
.container {
h1 {
left: 20px;
height: 60px;
img {
height: 30px;
}
}
.util {
right: 20px;
height: 60px;
}
}
}
#gnb {
top: 60px;
.list {
> li {
.depth01 {
height: 60px;
}
}
}
}
화면이 모바일만큼 작아지면 너무 붕 떠보이지 않게 높이를 다시 지정해주는 작업이 필요하다.
사용한 jquery
const tab = $(".tab");
const tabMenu = $(".tab .menu li");
const tabContents = $(".tab .contents > li");
tabMenu.on("click", function () {
const idx = $(this).index();
console.log("🚀 ~ file: common.js ~ line 7 ~ idx", idx);
const siblings = $(this).siblings();
$(this).addClass("on");
siblings.removeClass("on");
const selectedContents = tabContents.eq(idx);
const contentsSiblings = selectedContents.siblings();
selectedContents.addClass("on");
contentsSiblings.removeClass("on");
});
const btnAll = $(".btn-all");
const gnb = $("#gnb");
btnAll.on("click", function (e) {
e.preventDefault();
const icon = $(this).find(".material-icons");
//console.log(icon.text());
// if (icon.text() === "menu") {
// icon.text("close");
// console.log("close");
// } else {
// icon.text("menu");
// console.log("menu");
// }
if (gnb.hasClass("on")) {
icon.text("menu");
} else {
icon.text("close");
}
gnb.toggleClass("on");
});
//js에서 크기를 제어해야한다.
$(window).on("resize", function () {
console.log($(window).width());
const w = $(window).outerWidth();
console.log(w);
if (w > 1280) {
$("html").removeClass("m").addClass("pc");
$("#gnb .depth02").removeAttr("style");
$("#gnb").removeClass("on");
const icon = $(".btn-all .material-icons");
icon.text("menu");
// gnb에 on을 제거하고 btn-all안에 있는 material-icons의 텍스트 menu로 바꾸기...
} else {
$("html").removeClass("pc").addClass("m");
}
});
const depth01 = $("#gnb .depth01");
depth01.on("click", function (e) {
if ($("html").hasClass("m")) {
const depth02 = $(this).next();
const siblings = $(this).parent().siblings().find(".depth02");
const siblingsDepth01 = $(this).parent().siblings().find(".depth01");
$(this).toggleClass("on");
siblingsDepth01.removeClass("on");
siblings.slideUp();
if (depth02.length > 0) {
e.preventDefault();
depth02.stop().slideToggle();
}
}
});
$(window).trigger("resize");
- 주의 : 미디어쿼리로 아무리 padding을 줘도 기존 css 레이아웃이 우선 적용이 되어버리는 상황이 자주 발생한다. 이것은 css가 뒤에 적은 것이 우선 적용되는 게 아니라 더 복잡하게 클래스가 세분화된 항목 우선으로 적용하기 때문. 중복되지 않은 항목은 괜찮지만, 위에서 padding: 80px고 미디어쿼리에 120px로 적었는데 전자만 적용되는 경우가 종종 발생. 이럴 때는 앞의 클래스 조건이 더 세분화되어 있지 않은지 확인하고, .container을 header 아래에 넣는 등 배치를 좀 더 세밀하게 해주면 된다.
- 반응형 만들 거면 head 부분 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 옆에 minimum-scale=1.0, maximum-scale=1.0, user-scalable=no 라고 적어주고 시작해야 됨.
- style은 removeAttr("style") 로 지움.
- flex-shrink는 보통 디폴트가 1이라서 플렉스처리하면 같이 줄어들지만, flex-shrink:0이면 전혀 줄어들지 않음.
- position: static은 기존포지션을 없애줌...
- 넓이가 없을 때(auto)일 때 패딩을 주면 줄어듬. 넓이가 100%일때 패딩이나 마진이면 늘어남...
- subcontents에 연결해서 써서 컴파일 할 필요 없는 subpage scss 파일들은 앞에 _ 붙여둠.
'CSS' 카테고리의 다른 글
modules.css에서 var(--) CSS 변수 인식 못함(feat.CSS Variable Autocomplete) (0) | 2024.12.02 |
---|---|
[SCSS]글자에 패턴 넣기 (0) | 2022.11.16 |
[SCSS]Sass 컴파일러 깔기, 기초 작용법 (0) | 2022.10.04 |
[Font]구글 폰트, 눈누에서 웹 폰트 받아오는 법 (1) | 2022.09.30 |