add habit leveling/XP system, explicit fail marking for missed habits, and scrollable habit tabs
- Habits and the account now accumulate XP and levels from check streaks, with streak/level-up celebration banners and push notifications. - The "yesterday missed" banner on /today now lets a habit be explicitly marked failed (not just checked done), backed by a new HabitLog.status column so completion-rate/streak calculations never count a failed day as done. - The habit management tabs scroll horizontally on narrow screens instead of wrapping to two lines, with a pure-CSS edge shadow indicating more content.
This commit is contained in:
@@ -344,13 +344,30 @@ label {
|
||||
min-height: 1em;
|
||||
}
|
||||
|
||||
/* 탭 */
|
||||
/* 탭 — 좁은 화면에서 4개가 다 안 들어가면 줄바꿈 대신 가로 스크롤.
|
||||
양 끝에 스크롤 가능 방향으로만 그림자가 지도록 하는 순수 CSS 트릭:
|
||||
앞쪽 두 레이어(배경색→투명 그라데이션, background-attachment: local)는 콘텐츠와 같이 스크롤되면서
|
||||
탭 텍스트를 가려 페이드아웃시키고, 뒤쪽 두 레이어(radial-gradient 그림자, attachment: scroll)는
|
||||
스크롤 컨테이너 기준으로 고정되어 있다 — 콘텐츠 레이어가 그 위를 덮을 수 있을 때만(=그 방향으로
|
||||
더 스크롤할 내용이 있을 때만) 그림자가 드러나 보인다. 끝까지 스크롤하면 콘텐츠 레이어가 그림자를
|
||||
완전히 덮어 자연스럽게 사라진다. */
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
gap: 6px;
|
||||
margin-bottom: var(--space-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background:
|
||||
linear-gradient(to right, var(--color-bg) 50%, transparent) left,
|
||||
linear-gradient(to left, var(--color-bg) 50%, transparent) right,
|
||||
radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.2), transparent) left,
|
||||
radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.2), transparent) right;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--color-bg);
|
||||
background-size: 20px 100%, 20px 100%, 10px 100%, 10px 100%;
|
||||
background-attachment: local, local, scroll, scroll;
|
||||
}
|
||||
|
||||
.tabs a {
|
||||
@@ -360,6 +377,9 @@ label {
|
||||
padding: 10px 4px;
|
||||
margin-right: var(--space-2);
|
||||
border-bottom: 2px solid transparent;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.tabs a.active {
|
||||
@@ -603,6 +623,72 @@ label {
|
||||
}
|
||||
}
|
||||
|
||||
/* 레벨 배지 & XP 진행바 */
|
||||
.level-badge {
|
||||
background: var(--color-gold-tint);
|
||||
color: var(--color-gold);
|
||||
border-color: var(--color-gold);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.xp-bar-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.xp-bar {
|
||||
flex: 1 1 auto;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xp-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
.xp-bar-label {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.account-level-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.account-level-card .level-badge {
|
||||
font-size: 13px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.account-level-card .xp-bar-wrap {
|
||||
flex: 1 1 auto;
|
||||
max-width: 220px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.nav-level-pill {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-gold-tint);
|
||||
color: var(--color-gold);
|
||||
border: 1px solid var(--color-gold);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 월별 캘린더 */
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user