반응형
이번 포스트에서는 JavaScript와 CSS를 활용해 (다크, 라이트, 시스템 테마) 전환 기능과 함께 인디케이터 애니메이션 효과를 구현하는 방법을 저장하려 합니다. 각 단계를 간략히라도 설명 했고 다음에 사용하기 좋게 만들었습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>테마 전환 예제</title>
<style>
/* 기본 스타일 및 애니메이션 효과 */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
transition: background-color 0.3s, color 0.3s;
}
/* 라이트 테마 (기본) */
.light {
background-color: #fff;
color: #000;
}
/* 다크 테마 */
.dark {
background-color: #333;
color: #fff;
}
/* 시스템 테마 예시 (여기서는 중간 톤) */
.system {
background-color: #f0f0f0;
color: #000;
}
/* 테마 버튼 컨테이너 */
.theme-container {
position: relative;
width: 300px;
margin: 20px auto;
display: flex;
justify-content: space-between;
border: 1px solid #ccc;
border-radius: 30px;
overflow: hidden;
}
/* 테마 버튼 */
.theme-button {
flex: 1;
padding: 10px 0;
text-align: center;
cursor: pointer;
z-index: 2;
user-select: none;
}
/* 인디케이터: 버튼 아래로 이동하며 선택된 테마를 표시 */
#indicator {
position: absolute;
bottom: 0;
width: 33.33%; /* 3개의 버튼이므로 100/3% */
height: 3px;
background-color: #007BFF;
transition: transform 0.3s;
z-index: 1;
}
</style>
</head>
<body class="light">
<h1>테마 전환 예제</h1>
<!-- 테마 선택 컨테이너 -->
<div class="theme-container">
<div class="theme-button" data-theme="Light">Light</div>
<div class="theme-button" data-theme="Dark">Dark</div>
<div class="theme-button" data-theme="System">System</div>
<div id="indicator"></div>
</div>
<p>본문 내용이 여기에 표시됩니다.</p>
<script>
// 인디케이터 요소와 테마 버튼들을 선택합니다.
const indicator = document.getElementById("indicator");
const buttons = document.querySelectorAll(".theme-button");
// 현재 선택된 테마 (기본값: Light)
let currentTheme = "Light";
/**
* 인디케이터의 위치를 설정하는 함수
* @param {string} theme - "Light", "Dark", "System" 중 하나의 테마명
* @param {boolean} isInitial - 초기 로드시 true, 버튼 클릭시 false
*/
function setIndicatorPosition(theme, isInitial = false) {
// 각 테마별 초기 오프셋을 퍼센트 단위로 지정합니다.
const themeOffsets = {
Light: "0%", // 'px'도 사용 가능
Dark: "33.33%",
System: "66.66%"
};
// 초기 오프셋 값 가져오기
const offsetX = themeOffsets[theme];
if (isInitial) {
// 초기 로드 시 지정된 오프셋으로 이동
indicator.style.transform = `translateX(${offsetX})`;
} else {
// 버튼 클릭 시, 버튼의 실제 위치로 인디케이터를 이동시키는 로직
const themePositions = {
Light: 0,
Dark: 1,
System: 2,
};
const selectedButton = buttons[themePositions[theme]];
if (selectedButton) {
const buttonRect = selectedButton.getBoundingClientRect();
const parentRect = selectedButton.parentElement.getBoundingClientRect();
// 부모 요소 기준으로 버튼의 왼쪽 오프셋 계산
const calculatedOffsetX = buttonRect.left - parentRect.left;
indicator.style.transform = `translateX(${calculatedOffsetX}px)`;
}
}
}
/**
* 테마를 변경하는 함수
* @param {string} theme - "Light", "Dark", "System" 중 하나의 테마명
*/
function changeTheme(theme) {
currentTheme = theme;
// body 클래스 변경을 통해 테마 적용 (소문자 클래스 사용)
document.body.className = theme.toLowerCase();
// 인디케이터 위치 업데이트
setIndicatorPosition(theme);
// 필요 시, 로컬스토리지에 저장하거나 추가 작업 가능
console.log("현재 테마:", theme);
}
// 각 버튼에 클릭 이벤트를 등록합니다.
buttons.forEach(button => {
button.addEventListener("click", function() {
const selectedTheme = this.getAttribute("data-theme");
changeTheme(selectedTheme);
});
});
// 페이지 초기 로드 시 기본 테마에 따른 인디케이터 위치 설정
window.addEventListener("DOMContentLoaded", function() {
setIndicatorPosition(currentTheme, true);
});
</script>
</body>
</html>
JavaScript 동작 설명
setIndicatorPosition()
함수는 초기 로드와 버튼 클릭 시 인디케이터의 위치를 설정합니다. 초기 로드에서는 퍼센트 단위 오프셋을 사용하고, 클릭 시 실제 버튼의 위치를 계산합니다.changeTheme()
함수는 선택된 테마에 따라<body>
의 클래스를 변경하여 스타일을 적용하고, 인디케이터를 이동시킵니다.- 각 버튼에는 클릭 이벤트 리스너가 등록되어 있어 클릭 시 해당 테마로 전환됩니다.
- 페이지가 로드되면 기본 테마에 따른 인디케이터 위치가 설정됩니다.
반응형