웹사이트나 애플리케이션을 개발할 때 가장 중요한 작업 중 하나는 일관된 사용자 인터페이스(UI)를 만드는 것입니다. 잘 디자인된 UI 요소는 사용자 경험을 향상시키고, 브랜드 아이덴티티를 강화하며, 개발 워크플로우를 효율적으로 만들어 줍니다. 이 포스트에서는 가장 일반적인 UI 요소인 버튼, 네비게이션 바, 카드, 폼 등을 스타일링하는 방법과 모범 사례를 자세히 알아보겠습니다.
UI 스타일링의 중요성
효과적인 UI 요소 스타일링은 다음과 같은 이유로 중요합니다:
- 일관성: 일관된 디자인 언어는 사용자가 인터페이스를 직관적으로 이해하는 데 도움을 줍니다.
- 브랜딩: 고유한 스타일은 브랜드 아이덴티티를 강화합니다.
- 사용성: 잘 디자인된 UI 요소는 사용자 경험을 향상시킵니다.
- 유지보수성: 스타일 가이드와 컴포넌트 라이브러리는 개발 과정을 간소화합니다.
- 접근성: 적절한 스타일링은 웹 접근성을 개선합니다.
이제 각 UI 요소별로 스타일링 방법을 살펴보겠습니다.
버튼 디자인하기
버튼은 웹사이트에서 가장 기본적이고 중요한 상호작용 요소입니다. 효과적인 버튼 디자인은 사용자의 행동을 유도하는 데 큰 영향을 미칩니다.
기본 버튼 스타일
.button {
display: inline-block;
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
text-align: center;
text-decoration: none;
border-radius: 4px;
border: none;
cursor: pointer;
transition: all 0.3s ease;
background-color: #4CAF50; /* 기본 색상 */
color: white;
}
.button:hover {
background-color: #45a049;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.button:focus {
outline: 2px solid #2E7D32;
outline-offset: 2px;
}
버튼 종류별 스타일
/* 주요 버튼 */
.button-primary {
background-color: #4CAF50;
color: white;
}
/* 보조 버튼 */
.button-secondary {
background-color: #E0E0E0;
color: #333;
}
/* 위험 버튼 */
.button-danger {
background-color: #F44336;
color: white;
}
/* 아웃라인 버튼 */
.button-outline {
background-color: transparent;
color: #4CAF50;
border: 2px solid #4CAF50;
}
/* 비활성화 버튼 */
.button:disabled,
.button.disabled {
background-color: #CCCCCC;
color: #888888;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
버튼 크기 변형
/* 작은 버튼 */
.button-small {
padding: 8px 16px;
font-size: 14px;
}
/* 큰 버튼 */
.button-large {
padding: 16px 32px;
font-size: 18px;
}
/* 전체 너비 버튼 */
.button-full {
display: block;
width: 100%;
}
아이콘 포함 버튼
<button class="button button-with-icon">
<span class="icon">➕</span>
<span class="text">새 항목 추가</span>
</button>
.button-with-icon {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.button-with-icon .icon {
font-size: 1.2em;
}
네비게이션 바 스타일링
네비게이션 바는 웹사이트의 사용자 경험에 핵심적인 역할을 합니다. 직관적이고 반응형 네비게이션을 만드는 방법을 알아보겠습니다.
기본 네비게이션 바
<nav class="navbar">
<div class="navbar-logo">
<a href="/">Logo</a>
</div>
<ul class="navbar-menu">
<li class="navbar-item"><a href="/" class="navbar-link active">홈</a></li>
<li class="navbar-item"><a href="/about" class="navbar-link">소개</a></li>
<li class="navbar-item"><a href="/services" class="navbar-link">서비스</a></li>
<li class="navbar-item"><a href="/contact" class="navbar-link">문의</a></li>
</ul>
<button class="navbar-toggle">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</nav>
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 30px;
background-color: #ffffff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 1000;
}
.navbar-logo a {
font-size: 24px;
font-weight: 700;
color: #333;
text-decoration: none;
}
.navbar-menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.navbar-item {
margin-left: 20px;
}
.navbar-link {
color: #555;
text-decoration: none;
font-weight: 500;
padding: 5px 0;
position: relative;
transition: color 0.3s ease;
}
.navbar-link:hover {
color: #4CAF50;
}
.navbar-link.active {
color: #4CAF50;
}
/* 활성 링크에 밑줄 효과 */
.navbar-link.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: #4CAF50;
}
/* 모바일 메뉴 토글 버튼 */
.navbar-toggle {
display: none;
flex-direction: column;
background: none;
border: none;
cursor: pointer;
padding: 5px;
}
.icon-bar {
display: block;
width: 25px;
height: 3px;
margin: 3px 0;
background-color: #333;
transition: all 0.3s ease;
}
반응형 네비게이션 바
/* 반응형 스타일 */
@media (max-width: 768px) {
.navbar-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
flex-direction: column;
background-color: #ffffff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
.navbar-menu.show {
display: flex;
}
.navbar-item {
margin: 10px 0;
}
.navbar-toggle {
display: flex;
}
/* 햄버거 메뉴 애니메이션 */
.navbar-toggle.active .icon-bar:nth-child(1) {
transform: translateY(9px) rotate(45deg);
}
.navbar-toggle.active .icon-bar:nth-child(2) {
opacity: 0;
}
.navbar-toggle.active .icon-bar:nth-child(3) {
transform: translateY(-9px) rotate(-45deg);
}
}
// 모바일 메뉴 토글 스크립트
document.addEventListener('DOMContentLoaded', function() {
const navbarToggle = document.querySelector('.navbar-toggle');
const navbarMenu = document.querySelector('.navbar-menu');
navbarToggle.addEventListener('click', function() {
navbarMenu.classList.toggle('show');
navbarToggle.classList.toggle('active');
});
});
카드 컴포넌트 만들기
카드는 콘텐츠를 구조화하고 시각적으로 구분하는 데 매우 유용한 UI 요소입니다. 다양한 스타일의 카드를 만들어 보겠습니다.
기본 카드 컴포넌트
<div class="card">
<div class="card-image">
<img src="image.jpg" alt="카드 이미지">
</div>
<div class="card-content">
<h3 class="card-title">카드 제목</h3>
<p class="card-description">카드에 대한 간략한 설명 텍스트입니다. 이 부분에 카드의 내용을 요약하여 표시합니다.</p>
</div>
<div class="card-footer">
<button class="button button-small">자세히 보기</button>
</div>
</div>
.card {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
width: 100%;
max-width: 350px;
margin: 15px;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
.card-image {
width: 100%;
height: 200px;
overflow: hidden;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.card:hover .card-image img {
transform: scale(1.05);
}
.card-content {
padding: 20px;
}
.card-title {
margin: 0 0 10px 0;
font-size: 20px;
color: #333;
}
.card-description {
color: #666;
line-height: 1.5;
margin-bottom: 15px;
}
.card-footer {
padding: 15px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
}
다양한 카드 변형
/* 수평 카드 */
.card-horizontal {
display: flex;
max-width: 700px;
}
.card-horizontal .card-image {
width: 200px;
height: auto;
}
.card-horizontal .card-content {
flex: 1;
}
/* 미니 카드 */
.card-mini {
max-width: 250px;
}
.card-mini .card-image {
height: 120px;
}
.card-mini .card-content {
padding: 15px;
}
.card-mini .card-title {
font-size: 16px;
}
.card-mini .card-description {
font-size: 14px;
}
/* 전체 링크 카드 */
.card-link {
position: relative;
}
.card-link a.card-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
text-indent: -9999px;
}
/* 카드 그리드 레이아웃 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 30px;
padding: 20px;
}
폼 요소 스타일링
폼은 사용자 상호작용의 핵심입니다. 시각적으로 매력적이고 사용하기 쉬운 폼 요소를 만들어 보겠습니다.
입력 필드 스타일링
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
.form-input,
.form-textarea,
.form-select {
display: block;
width: 100%;
padding: 12px 15px;
font-size: 16px;
line-height: 1.5;
color: #333;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.form-input:focus,
.form-textarea:focus,
.form-select:focus {
border-color: #4CAF50;
outline: none;
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.2);
}
.form-textarea {
min-height: 100px;
resize: vertical;
}
.form-select {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23333' viewBox='0 0 16 16'%3E%3Cpath d='M8 12l-6-6h12z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 15px center;
padding-right: 40px;
}
/* 도움말 텍스트 */
.form-help {
display: block;
margin-top: 5px;
font-size: 14px;
color: #666;
}
/* 오류 상태 */
.form-input.error,
.form-textarea.error,
.form-select.error {
border-color: #F44336;
}
.form-error-message {
color: #F44336;
font-size: 14px;
margin-top: 5px;
}
체크박스와 라디오 버튼
<!-- 체크박스 -->
<div class="form-checkbox">
<input type="checkbox" id="checkbox1" class="checkbox-input">
<label for="checkbox1" class="checkbox-label">이용약관에 동의합니다</label>
</div>
<!-- 라디오 버튼 -->
<div class="form-radio">
<input type="radio" id="radio1" name="option" class="radio-input">
<label for="radio1" class="radio-label">옵션 1</label>
</div>
<div class="form-radio">
<input type="radio" id="radio2" name="option" class="radio-input">
<label for="radio2" class="radio-label">옵션 2</label>
</div>
/* 체크박스 커스텀 스타일 */
.form-checkbox,
.form-radio {
display: flex;
align-items: center;
margin-bottom: 15px;
cursor: pointer;
}
.checkbox-input,
.radio-input {
position: absolute;
opacity: 0;
height: 0;
width: 0;
}
.checkbox-label,
.radio-label {
position: relative;
padding-left: 35px;
cursor: pointer;
font-size: 16px;
line-height: 24px;
user-select: none;
}
.checkbox-label::before,
.radio-label::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
border: 2px solid #ddd;
background-color: #fff;
transition: all 0.3s ease;
}
.checkbox-label::before {
border-radius: 4px;
}
.radio-label::before {
border-radius: 50%;
}
.checkbox-input:checked + .checkbox-label::before {
background-color: #4CAF50;
border-color: #4CAF50;
}
.radio-input:checked + .radio-label::before {
border-color: #4CAF50;
border-width: 6px;
}
.checkbox-input:checked + .checkbox-label::after {
content: '';
position: absolute;
left: 7px;
top: 3px;
width: 6px;
height: 12px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.checkbox-input:focus + .checkbox-label::before,
.radio-input:focus + .radio-label::before {
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.2);
}
토글 스위치
<label class="switch">
<input type="checkbox" class="switch-input">
<span class="switch-slider"></span>
<span class="switch-label">알림 설정</span>
</label>
.switch {
position: relative;
display: inline-flex;
align-items: center;
cursor: pointer;
}
.switch-input {
opacity: 0;
width: 0;
height: 0;
}
.switch-slider {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
background-color: #ddd;
border-radius: 24px;
transition: all 0.3s ease;
margin-right: 10px;
}
.switch-slider:before {
content: '';
position: absolute;
height: 20px;
width: 20px;
left: 2px;
bottom: 2px;
background-color: white;
border-radius: 50%;
transition: all 0.3s ease;
}
.switch-input:checked + .switch-slider {
background-color: #4CAF50;
}
.switch-input:checked + .switch-slider:before {
transform: translateX(26px);
}
.switch-input:focus + .switch-slider {
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.2);
}
.switch-label {
font-size: 16px;
}
알림과 모달 창
알림과 모달은 사용자에게 중요한 정보를 전달하거나 집중적인 작업을 요구할 때 사용합니다.
알림 메시지
<div class="alert alert-success">
<div class="alert-icon">✓</div>
<div class="alert-content">
<h4 class="alert-title">성공!</h4>
<p class="alert-message">변경사항이 성공적으로 저장되었습니다.</p>
</div>
<button class="alert-close">×</button>
</div>
<div class="alert alert-error">
<div class="alert-icon">!</div>
<div class="alert-content">
<h4 class="alert-title">오류 발생</h4>
<p class="alert-message">저장 중 문제가 발생했습니다. 다시 시도해주세요.</p>
</div>
<button class="alert-close">×</button>
</div>
.alert {
display: flex;
align-items: flex-start;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
position: relative;
}
.alert-success {
background-color: #E8F5E9;
border-left: 4px solid #4CAF50;
}
.alert-info {
background-color: #E3F2FD;
border-left: 4px solid #2196F3;
}
.alert-warning {
background-color: #FFF8E1;
border-left: 4px solid #FFC107;
}
.alert-error {
background-color: #FFEBEE;
border-left: 4px solid #F44336;
}
.alert-icon {
font-size: 20px;
margin-right: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
}
.alert-success .alert-icon {
color: #4CAF50;
}
.alert-error .alert-icon {
color: #F44336;
}
.alert-content {
flex: 1;
}
.alert-title {
margin: 0 0 5px 0;
font-size: 16px;
font-weight: 600;
}
.alert-message {
margin: 0;
font-size: 14px;
}
.alert-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #999;
padding: 0;
line-height: 1;
}
.alert-close:hover {
color: #333;
}
모달 창
<button class="button" id="openModal">모달 열기</button>
<div class="modal" id="myModal">
<div class="modal-overlay"></div>
<div class="modal-container">
<div class="modal-header">
<h3 class="modal-title">모달 제목</h3>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>모달 내용이 여기에 들어갑니다. 다양한 컨텐츠를 포함할 수 있습니다.</p>
</div>
<div class="modal-footer">
<button class="button button-secondary" id="closeModal">취소</button>
<button class="button button-primary">확인</button>
</div>
</div>
</div>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1050;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.modal.active {
opacity: 1;
visibility: visible;
}
.modal-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: -1;
}
.modal-container {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
width: 100%;
max-width: 500px;
transform: translateY(20px);
transition: transform 0.3s ease;
}
.modal.active .modal-container {
transform: translateY(0);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.modal-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
padding: 0;
line-height: 1;
}
.modal-body {
padding: 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
padding: 15px 20px;
border-top: 1px solid #eee;
gap: 10px;
}
// 모달 제어 스크립트
document.addEventListener('DOMContentLoaded', function() {
const modal = document.getElementById('myModal');
const openModalBtn = document.getElementById('openModal');
const closeModalBtn = document.getElementById('closeModal');
const modalClose = document.querySelector('.modal-close');
const modalOverlay = document.querySelector('.modal-overlay');
// 모달 열기
openModalBtn.addEventListener('click', function() {
modal.classList.add('active');
document.body.style.overflow = 'hidden'; // 스크롤 방지
});
// 모달 닫기 함수
function closeModal() {
modal.classList.remove('active');
document.body.style.overflow = ''; // 스크롤 복원
}
// 다양한 방법으로 모달 닫기
closeModalBtn.addEventListener('click', closeModal);
modalClose.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', closeModal);
// ESC 키로 모달 닫기
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && modal.classList.contains('active')) {
closeModal();
}
});
});
아코디언과 탭
정보를 구조화하고 공간을 절약하기 위한 아코디언과 탭 컴포넌트를 만들어 보겠습니다.
아코디언 컴포넌트
<div class="accordion">
<div class="accordion-item">
<button class="accordion-header">
<span class="accordion-title">섹션 1</span>
<span class="accordion-icon">+</span>
</button>
<div class="accordion-content">
<p>첫 번째 섹션의 내용입니다. 여기에 텍스트, 이미지, 폼 등 다양한 콘텐츠가 들어갈 수 있습니다.</p>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header">
<span class="accordion-title">섹션 2</span>
<span class="accordion-icon">+</span>
</button>
<div class="accordion-content">
<p>두 번째 섹션의 내용입니다. 아코디언은 콘텐츠를 구조화하고 공간을 절약하는데 유용합니다.</p>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header">
<span class="accordion-title">섹션 3</span>
<span class="accordion-icon">+</span>
</button>
<div class="accordion-content">
<p>세 번째 섹션의 내용입니다. 사용자는 관심 있는 섹션만 열어서 볼 수 있습니다.</p>
</div>
</div>
</div>
.accordion {
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.accordion-item {
border-bottom: 1px solid #ddd;
}
.accordion-item:last-child {
border-bottom: none;
}
.accordion-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 15px 20px;
background-color: #f7f7f7;
border: none;
text-align: left;
cursor: pointer;
transition: background-color 0.3s ease;
}
.accordion-header:hover {
background-color: #f0f0f0;
}
.accordion-title {
font-weight: 600;
font-size: 16px;
}
.accordion-icon {
font-size: 18px;
transition: transform 0.3s ease;
}
.accordion-item.active .accordion-icon {
transform: rotate(45deg);
}
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
padding: 0 20px;
}
.accordion-item.active .accordion-content {
max-height: 1000px; /* 충분히 큰 값 */
padding: 20px;
}
// 아코디언 기능 구현
document.addEventListener('DOMContentLoaded', function() {
const accordionHeaders = document.querySelectorAll('.accordion-header');
accordionHeaders.forEach(header => {
header.addEventListener('click', function() {
const accordionItem = this.parentElement;
accordionItem.classList.toggle('active');
// 다른 아이템 닫기 (선택 사항)
if (accordionItem.classList.contains('active')) {
accordionHeaders.forEach(otherHeader => {
if (otherHeader !== header) {
otherHeader.parentElement.classList.remove('active');
}
});
}
});
});
});
탭 컴포넌트
<div class="tabs">
<div class="tab-list">
<button class="tab-button active" data-tab="tab1">탭 1</button>
<button class="tab-button" data-tab="tab2">탭 2</button>
<button class="tab-button" data-tab="tab3">탭 3</button>
</div>
<div class="tab-panels">
<div class="tab-panel active" id="tab1">
<h3>첫 번째 탭</h3>
<p>첫 번째 탭의 내용입니다.</p>
</div>
<div class="tab-panel" id="tab2">
<h3>두 번째 탭</h3>
<p>두 번째 탭의 내용입니다.</p>
</div>
<div class="tab-panel" id="tab3">
<h3>세 번째 탭</h3>
<p>세 번째 탭의 내용입니다.</p>
</div>
</div>
</div>
.tabs {
margin: 30px 0;
}
.tab-list {
display: flex;
border-bottom: 1px solid #ddd;
}
.tab-button {
padding: 10px 20px;
background-color: transparent;
border: none;
cursor: pointer;
font-size: 16px;
font-weight: 500;
position: relative;
transition: all 0.3s ease;
}
.tab-button:hover {
color: #4CAF50;
}
.tab-button.active {
color: #4CAF50;
}
.tab-button.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 2px;
background-color: #4CAF50;
}
.tab-panel {
display: none;
padding: 20px 0;
}
.tab-panel.active {
display: block;
}
// 탭 기능 구현
document.addEventListener('DOMContentLoaded', function() {
const tabButtons = document.querySelectorAll('.tab-button');
tabButtons.forEach(button => {
button.addEventListener('click', function() {
// 활성 탭 버튼 업데이트
tabButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
// 활성 탭 패널 업데이트
const tabId = this.getAttribute('data-tab');
const tabPanels = document.querySelectorAll('.tab-panel');
tabPanels.forEach(panel => panel.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
});
});
});
테이블 스타일링
데이터를 효과적으로 표시하기 위한 테이블 스타일링 방법을 알아보겠습니다.
기본 테이블 스타일
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>이메일</th>
<th>상태</th>
<th>작업</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>홍길동</td>
<td>hong@example.com</td>
<td><span class="badge badge-success">활성</span></td>
<td>
<button class="button button-small">편집</button>
</td>
</tr>
<tr>
<td>2</td>
<td>김철수</td>
<td>kim@example.com</td>
<td><span class="badge badge-warning">대기</span></td>
<td>
<button class="button button-small">편집</button>
</td>
</tr>
<tr>
<td>3</td>
<td>이영희</td>
<td>lee@example.com</td>
<td><span class="badge badge-danger">비활성</span></td>
<td>
<button class="button button-small">편집</button>
</td>
</tr>
</tbody>
</table>
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 16px;
}
.table thead th {
background-color: #f5f5f5;
color: #333;
font-weight: 600;
text-align: left;
padding: 12px 15px;
border-bottom: 2px solid #ddd;
}
.table tbody tr {
border-bottom: 1px solid #ddd;
transition: background-color 0.3s ease;
}
.table tbody tr:hover {
background-color: #f9f9f9;
}
.table tbody td {
padding: 12px 15px;
vertical-align: middle;
}
/* 배지 스타일 */
.badge {
display: inline-block;
padding: 5px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
text-align: center;
}
.badge-success {
background-color: #E8F5E9;
color: #4CAF50;
}
.badge-warning {
background-color: #FFF8E1;
color: #FFC107;
}
.badge-danger {
background-color: #FFEBEE;
color: #F44336;
}
반응형 테이블
/* 모바일용 반응형 테이블 */
@media (max-width: 768px) {
.table {
border: 0;
}
.table thead {
display: none;
}
.table tbody tr {
display: block;
margin-bottom: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
.table tbody td {
display: flex;
justify-content: space-between;
padding: 10px 15px;
text-align: right;
border-bottom: 1px solid #eee;
}
.table tbody td:before {
content: attr(data-label);
font-weight: 600;
float: left;
text-align: left;
}
.table tbody td:last-child {
border-bottom: 0;
}
}
모바일용 테이블을 사용하려면 HTML에 데이터 라벨을 추가해야 합니다:
<td data-label="ID">1</td>
<td data-label="이름">홍길동</td>
<td data-label="이메일">hong@example.com</td>
접근성 고려사항
UI 요소를 디자인할 때 접근성을 고려하는 것은 매우 중요합니다. 모든 사용자가 콘텐츠에 접근하고 상호작용할 수 있도록 해야 합니다.
키보드 접근성
버튼, 링크, 폼 요소 등 모든 상호작용 요소는 키보드로 접근 가능해야 합니다:
/* 포커스 스타일 */
:focus {
outline: 2px solid #4CAF50;
outline-offset: 2px;
}
/* 포커스만 보이는 요소 (스크린 리더 지원) */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* 포커스 시에만 표시되는 요소 */
.sr-only-focusable:not(:focus) {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
대비와 가독성
텍스트와 배경 간의 충분한 대비를 유지하세요. WCAG 지침에 따르면 일반 텍스트의 경우 최소 4.5:1, 대형 텍스트의 경우 최소 3:1의 대비가 필요합니다.
/* 높은 대비를 위한 색상 */
:root {
--text-color: #333; /* 어두운 텍스트 */
--bg-color: #fff; /* 밝은 배경 */
--primary-color: #2E7D32; /* 접근성이 높은 녹색 */
}
body {
color: var(--text-color);
background-color: var(--bg-color);
}
/* 대비를 위한 링크 스타일 */
a {
color: var(--primary-color);
text-decoration: underline; /* 색상 외에도 구분 가능 */
}
ARIA 속성 활용
복잡한 UI 컴포넌트에는 적절한 ARIA 속성을 사용하여 접근성을 향상시킵니다:
<!-- 접근성이 향상된 모달 -->
<div class="modal" id="myModal" role="dialog" aria-labelledby="modalTitle" aria-modal="true">
<div class="modal-container">
<div class="modal-header">
<h3 class="modal-title" id="modalTitle">모달 제목</h3>
<button class="modal-close" aria-label="닫기">×</button>
</div>
<!-- 내용 생략 -->
</div>
</div>
<!-- 접근성이 향상된 아코디언 -->
<div class="accordion">
<div class="accordion-item">
<button class="accordion-header" aria-expanded="false" aria-controls="content1">
<span class="accordion-title">섹션 1</span>
<span class="accordion-icon" aria-hidden="true">+</span>
</button>
<div class="accordion-content" id="content1">
<p>내용...</p>
</div>
</div>
</div>
디자인 시스템 구축하기
여러 UI 요소를 일관된 디자인 시스템으로 통합하여 재사용 가능한 컴포넌트 라이브러리를 만드는 방법을 알아보겠습니다.
CSS 변수를 활용한 테마
:root {
/* 색상 */
--color-primary: #4CAF50;
--color-primary-dark: #3E8E41;
--color-primary-light: #A5D6A7;
--color-secondary: #2196F3;
--color-secondary-dark: #0D47A1;
--color-secondary-light: #BBDEFB;
--color-danger: #F44336;
--color-warning: #FFC107;
--color-success: #4CAF50;
--color-info: #2196F3;
/* 중립 색상 */
--color-text: #333;
--color-text-light: #666;
--color-background: #fff;
--color-background-light: #f5f5f5;
--color-border: #ddd;
/* 타이포그래피 */
--font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', sans-serif;
--font-size-xs: 12px;
--font-size-sm: 14px;
--font-size-md: 16px;
--font-size-lg: 18px;
--font-size-xl: 20px;
--font-size-xxl: 24px;
/* 간격 */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-xxl: 48px;
/* 테두리 */
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 16px;
--border-radius-full: 9999px;
/* 그림자 */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
/* 애니메이션 */
--transition-fast: 0.2s ease;
--transition-normal: 0.3s ease;
--transition-slow: 0.5s ease;
}
/* 다크 테마 */
@media (prefers-color-scheme: dark) {
:root {
--color-text: #f5f5f5;
--color-text-light: #aaa;
--color-background: #121212;
--color-background-light: #222;
--color-border: #444;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.3);
}
}
유틸리티 클래스
/* 유틸리티 클래스 */
/* 마진 */
.m-0 { margin: 0; }
.m-xs { margin: var(--spacing-xs); }
.m-sm { margin: var(--spacing-sm); }
.m-md { margin: var(--spacing-md); }
.m-lg { margin: var(--spacing-lg); }
.m-xl { margin: var(--spacing-xl); }
.mt-0 { margin-top: 0; }
.mt-xs { margin-top: var(--spacing-xs); }
.mt-sm { margin-top: var(--spacing-sm); }
.mt-md { margin-top: var(--spacing-md); }
.mt-lg { margin-top: var(--spacing-lg); }
.mt-xl { margin-top: var(--spacing-xl); }
/* 패딩 */
.p-0 { padding: 0; }
.p-xs { padding: var(--spacing-xs); }
.p-sm { padding: var(--spacing-sm); }
.p-md { padding: var(--spacing-md); }
.p-lg { padding: var(--spacing-lg); }
.p-xl { padding: var(--spacing-xl); }
/* 텍스트 정렬 */
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
/* 디스플레이 */
.d-flex { display: flex; }
.d-block { display: block; }
.d-inline-block { display: inline-block; }
.d-none { display: none; }
/* Flex 유틸리티 */
.flex-column { flex-direction: column; }
.flex-row { flex-direction: row; }
.justify-start { justify-content: flex-start; }
.justify-center { justify-content: center; }
.justify-end { justify-content: flex-end; }
.justify-between { justify-content: space-between; }
.justify-around { justify-content: space-around; }
.align-start { align-items: flex-start; }
.align-center { align-items: center; }
.align-end { align-items: flex-end; }
/* 너비와 높이 */
.w-100 { width: 100%; }
.w-75 { width: 75%; }
.w-50 { width: 50%; }
.w-25 { width: 25%; }
.h-100 { height: 100%; }
.h-75 { height: 75%; }
.h-50 { height: 50%; }
.h-25 { height: 25%; }
컴포넌트 기반 구조
// SCSS를 사용한 컴포넌트 기반 구조 예시
// 변수
@import 'variables';
// 기본 스타일
@import 'base/reset';
@import 'base/typography';
@import 'base/utilities';
// 컴포넌트
@import 'components/buttons';
@import 'components/cards';
@import 'components/forms';
@import 'components/navbar';
@import 'components/modals';
@import 'components/alerts';
@import 'components/tables';
@import 'components/tabs';
@import 'components/accordion';
// 레이아웃
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
// 페이지
@import 'pages/home';
@import 'pages/about';
@import 'pages/contact';
마무리
이 가이드에서는 웹사이트와 애플리케이션에 필수적인 다양한 UI 요소의 스타일링 방법을 살펴보았습니다. 버튼, 네비게이션 바, 카드, 폼, 아코디언, 탭, 테이블 등 기본적인 컴포넌트부터 시작하여 접근성과 디자인 시스템 구축까지 다루었습니다.
효과적인 UI 요소 디자인의 핵심 요약:
- 일관성: 색상, 간격, 폰트 등 모든 디자인 요소에 일관성을 유지합니다.
- 접근성: 모든 사용자가 콘텐츠에 접근할 수 있도록 디자인합니다.
- 반응형: 다양한 디바이스에서 잘 작동하도록 UI 요소를 설계합니다.
- 재사용성: 컴포넌트를 모듈화하여 유지보수와 확장이 쉽도록 합니다.
- 사용자 경험: 직관적이고 사용하기 쉬운 인터페이스를 제공합니다.
위 코드 예제와 가이드라인을 바탕으로 자신만의 디자인 시스템을 구축하고, 일관되고 전문적인 웹사이트나 애플리케이션을 만들 수 있습니다. 이 지식을 활용하여 여러분의 다음 프로젝트에서 더 나은 사용자 경험을 제공하세요!
이 가이드가 도움이 되었나요? 더 알고 싶은 UI 컴포넌트나 스타일링 기법이 있다면 댓글로 알려주세요. 다음 포스트에서는 애니메이션과 마이크로 인터랙션에 대해 알아보도록 하겠습니다.
'HTML&CSS > 실전 스타일링' 카테고리의 다른 글
실습: 반응형 블로그 메인 페이지 만들기 완벽 가이드 (0) | 2025.05.11 |
---|---|
애니메이션과 마이크로 인터랙션: 웹 경험을 극대화하는 완벽 가이드 (0) | 2025.05.11 |
모바일 우선 개발: 현대 웹 디자인의 필수 접근법 완벽 가이드 (0) | 2025.05.11 |
반응형 디자인 개념 완벽 가이드: Media Queries 마스터하기 (0) | 2025.05.11 |