Files
habit-tracker/app/static/css/style.css
T
shinalokandClaude Sonnet 5 d212451fe0 add habit goal-period difficulty and habit formation research notes
Ground the 21-day habit myth in actual research (Lally et al. 2010) and let
users pick a target period (21/66/254 days or unlimited) matching that
study's easy/median/hard automaticity timelines when creating a habit,
with progress shown on the habits list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 10:12:17 +09:00

675 lines
12 KiB
CSS

:root {
--color-bg: #f5f4ef;
--color-surface: #ffffff;
--color-text: #2b2a27;
--color-text-muted: #6b6a66;
--color-accent: #d97757;
--color-accent-hover: #c15f3c;
--color-accent-rgb: 217, 119, 87;
--color-border: #e8e6df;
--color-success: #4f7a5a;
--color-success-tint: rgba(79, 122, 90, 0.08);
--color-danger: #b3543f;
--color-gold: #a8791a;
--color-gold-tint: rgba(168, 121, 26, 0.12);
--radius-card: 14px;
--radius-control: 10px;
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04);
--space-1: 8px;
--space-2: 16px;
--space-3: 24px;
--space-4: 32px;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Pretendard", "Malgun Gothic", sans-serif;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #1f1e1b;
--color-surface: #2a2926;
--color-text: #edebe4;
--color-text-muted: #a8a69f;
--color-accent: #e08962;
--color-accent-hover: #eb9c78;
--color-accent-rgb: 224, 137, 98;
--color-border: #3a3833;
--color-success: #6fa47c;
--color-success-tint: rgba(111, 164, 124, 0.14);
--color-danger: #d97c68;
--color-gold: #d9b84f;
--color-gold-tint: rgba(217, 184, 79, 0.16);
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.2);
}
}
* {
box-sizing: border-box;
}
[x-cloak] {
display: none !important;
}
html,
body {
margin: 0;
padding: 0;
}
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-sans);
font-size: 15px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
}
.app-shell {
max-width: 640px;
margin: 0 auto;
padding: var(--space-3) var(--space-2) var(--space-4);
}
@media (min-width: 900px) {
.app-shell {
max-width: 760px;
padding-top: var(--space-4);
}
.app-shell.wide {
max-width: 960px;
}
}
h1,
h2,
h3 {
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.3;
margin: 0 0 var(--space-2);
}
h1 {
font-size: 22px;
}
h2 {
font-size: 17px;
}
p {
margin: 0 0 var(--space-2);
color: var(--color-text-muted);
}
/* iOS 홈 화면 추가 안내 배너 */
.ios-install-banner {
display: none;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
box-shadow: var(--shadow-card);
padding: 10px 12px;
margin-bottom: var(--space-2);
font-size: 13px;
color: var(--color-text);
}
.ios-install-banner button {
flex-shrink: 0;
background: none;
border: none;
color: var(--color-text-muted);
font-size: 18px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
/* 상단 네비게이션 */
.top-nav {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
row-gap: var(--space-1);
margin-bottom: var(--space-3);
}
.top-nav .brand {
font-weight: 600;
font-size: 17px;
white-space: nowrap;
}
.top-nav .nav-links {
display: flex;
gap: var(--space-1);
}
.top-nav .nav-links a {
text-decoration: none;
color: var(--color-text-muted);
font-size: 14px;
white-space: nowrap;
padding: 6px 10px;
border-radius: var(--radius-control);
}
.top-nav .nav-links a.active {
color: var(--color-text);
background: var(--color-surface);
box-shadow: var(--shadow-card);
}
.top-nav .nav-user {
display: flex;
align-items: center;
flex-shrink: 0;
gap: var(--space-1);
font-size: 13px;
color: var(--color-text-muted);
}
.top-nav .nav-user span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 96px;
}
.btn-link {
background: none;
border: none;
padding: 0;
font: inherit;
font-size: 13px;
color: var(--color-text-muted);
text-decoration: underline;
white-space: nowrap;
flex-shrink: 0;
cursor: pointer;
}
/* 하단 탭바 (모바일 전용) — 기본은 숨김, 좁은 화면에서만 노출 */
.bottom-tab-bar {
display: none;
}
/* 좁은 화면: 상단은 브랜드+유저정보만 남기고, 이동 네비게이션은 하단 탭바로 옮긴다 */
@media (max-width: 480px) {
.top-nav .nav-links {
display: none;
}
.app-shell {
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px) + var(--space-2));
}
.bottom-tab-bar {
display: flex;
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 40;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
padding-bottom: env(safe-area-inset-bottom, 0px);
}
.bottom-tab-bar .tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
padding: 8px 4px 6px;
text-decoration: none;
color: var(--color-text-muted);
font-size: 11px;
}
.bottom-tab-bar .tab-item svg {
width: 22px;
height: 22px;
}
.bottom-tab-bar .tab-item.active {
color: var(--color-accent);
}
}
/* 카드 */
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-card);
box-shadow: var(--shadow-card);
padding: var(--space-3);
margin-bottom: var(--space-2);
}
/* 버튼 */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
gap: 6px;
border: none;
border-radius: var(--radius-control);
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
font-family: inherit;
transition: background-color 0.15s ease, opacity 0.15s ease;
}
.btn-primary {
background: var(--color-accent);
color: #fff;
}
.btn-primary:hover {
background: var(--color-accent-hover);
}
.btn-secondary {
background: transparent;
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-bg);
}
.btn-danger-ghost {
background: transparent;
color: var(--color-danger);
border: 1px solid var(--color-border);
}
.btn-block {
width: 100%;
}
/* 입력 */
input[type="text"],
input[type="time"],
input[type="password"] {
width: 100%;
font-family: inherit;
font-size: 15px;
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
background: var(--color-bg);
color: var(--color-text);
}
label {
display: block;
font-size: 13px;
color: var(--color-text-muted);
margin-bottom: 6px;
}
.field {
margin-bottom: var(--space-2);
}
.form-error-text {
color: var(--color-danger);
font-size: 13px;
margin: -8px 0 var(--space-2);
min-height: 1em;
}
/* 탭 */
.tabs {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border);
}
.tabs a {
text-decoration: none;
color: var(--color-text-muted);
font-size: 14px;
padding: 10px 4px;
margin-right: var(--space-2);
border-bottom: 2px solid transparent;
}
.tabs a.active {
color: var(--color-text);
border-bottom-color: var(--color-accent);
font-weight: 600;
}
/* 요일 선택 pill */
.weekday-picker {
display: flex;
gap: 6px;
flex-wrap: wrap;
align-items: center;
}
.weekday-pill {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--color-border);
background: var(--color-bg);
color: var(--color-text);
font-size: 13px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.weekday-pill.selected {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
}
.everyday-btn {
border-radius: 999px;
border: 1px solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font-size: 13px;
padding: 8px 14px;
cursor: pointer;
margin-left: 4px;
}
.everyday-btn.selected {
border-color: var(--color-accent);
color: var(--color-accent);
}
/* 목표 기간 난이도 선택 */
.difficulty-picker {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.difficulty-pill {
border-radius: 999px;
border: 1px solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font-size: 13px;
padding: 8px 14px;
cursor: pointer;
}
.difficulty-pill.selected {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
}
.difficulty-hint {
font-size: 12px;
color: var(--color-text-muted);
margin-top: 6px;
}
.goal-progress-badge {
font-size: 12px;
color: var(--color-text-muted);
}
/* 습관 리스트 아이템 */
.habit-item {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px var(--space-2);
padding: 12px 4px;
border-bottom: 1px solid var(--color-border);
}
.habit-item:last-child {
border-bottom: none;
}
.habit-item.today-checked {
background: var(--color-success-tint);
border-radius: var(--radius-control);
margin: 0 -4px;
padding-left: 8px;
padding-right: 8px;
}
.habit-item-main {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
flex: 1 1 auto;
}
.habit-item-name {
font-size: 15px;
overflow-wrap: break-word;
word-break: break-word;
}
.habit-item-condition {
font-size: 13px;
color: var(--color-text-muted);
margin-top: 2px;
}
.habit-item-meta {
font-size: 12px;
color: var(--color-text-muted);
}
.habit-item-stats {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 6px;
}
.drag-handle {
flex-shrink: 0;
cursor: grab;
color: var(--color-text-muted);
font-size: 18px;
line-height: 1;
padding: 4px 2px;
touch-action: none;
user-select: none;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-chosen .habit-item {
background: var(--color-bg);
border-radius: var(--radius-control);
}
.habit-item-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.check-indicator {
width: 26px;
height: 26px;
padding: 0;
margin: 0;
border-radius: 50%;
border: 2px solid var(--color-border);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
flex-shrink: 0;
background: transparent;
font-family: inherit;
font-size: 13px;
line-height: 1;
transition: background-color 0.15s ease, border-color 0.15s ease;
}
.check-indicator.checked {
background: var(--color-success);
border-color: var(--color-success);
color: #fff;
}
.empty-state {
text-align: center;
color: var(--color-text-muted);
padding: var(--space-4) var(--space-2);
font-size: 14px;
}
.badge {
display: inline-block;
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: var(--color-bg);
color: var(--color-text-muted);
border: 1px solid var(--color-border);
}
.badge-milestone {
background: var(--color-gold-tint);
color: var(--color-gold);
border-color: var(--color-gold);
font-weight: 600;
}
.celebration-banner {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
margin-bottom: var(--space-2);
border-radius: var(--radius-card);
background: var(--color-gold-tint);
border: 1px solid var(--color-gold);
color: var(--color-text);
font-size: 14px;
animation: celebration-pop 0.4s ease-out;
}
@keyframes celebration-pop {
from {
opacity: 0;
transform: scale(0.95) translateY(-4px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* 월별 캘린더 */
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
margin-bottom: 4px;
}
.calendar-weekday {
text-align: center;
font-size: 12px;
color: var(--color-text-muted);
padding: 4px 0;
}
.calendar-cell {
aspect-ratio: 1;
border-radius: 8px;
border: 1px solid var(--color-border);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
}
.calendar-cell.muted {
opacity: 0.35;
}
.calendar-date {
font-size: 12px;
font-weight: 500;
}
.calendar-ratio {
font-size: 10px;
color: var(--color-text-muted);
}
/* 주별 매트릭스 */
.week-matrix {
width: 100%;
border-collapse: collapse;
font-size: 13px;
white-space: nowrap;
}
.week-matrix th,
.week-matrix td {
padding: 10px 8px;
text-align: center;
border-bottom: 1px solid var(--color-border);
}
.week-matrix th:first-child,
.week-matrix td:first-child {
text-align: left;
}
.week-matrix th {
color: var(--color-text-muted);
font-weight: 500;
font-size: 12px;
}
.wm-check {
color: var(--color-success);
font-weight: 700;
}
.wm-dash {
color: var(--color-border);
}