/* * Obsidian Halo Plugin - Design System * 基于三层设计令牌架构: Primitive → Semantic → Component */ /* ============================================ Primitive Tokens (原始值) ============================================ */ :root { /* Halo Brand Colors */ --halo-primary: #3b82f6; --halo-primary-hover: #2563eb; --halo-primary-active: #1d4ed8; --halo-primary-muted: rgba(59, 130, 246, 0.15); --halo-danger: #ef4444; --halo-danger-hover: #dc2626; --halo-danger-muted: rgba(239, 68, 68, 0.15); --halo-success: #22c55e; --halo-success-hover: #16a34a; --halo-success-muted: rgba(34, 197, 94, 0.15); --halo-warning: #f59e0b; --halo-warning-hover: #d97706; --halo-warning-muted: rgba(245, 158, 11, 0.15); /* Spacing Scale */ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; /* Border Radius */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* Shadows */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.15); --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.2); /* Typography */ --font-size-xs: 11px; --font-size-sm: 12px; --font-size-base: 14px; --font-size-lg: 16px; --font-size-xl: 18px; --font-size-2xl: 20px; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; /* Transitions */ --transition-fast: 150ms ease-in-out; --transition-normal: 200ms ease-out; --transition-slow: 300ms ease-in-out; /* Focus Ring */ --focus-ring-width: 2px; --focus-ring-offset: 2px; --focus-ring-color: var(--halo-primary); } /* ============================================ Component Tokens (组件级) ============================================ */ /* --- Button --- */ .halo-btn { --btn-height: 36px; --btn-padding-x: 16px; --btn-font-size: var(--font-size-base); --btn-radius: var(--radius-md); --btn-border-width: 1px; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); height: var(--btn-height); padding: 0 var(--btn-padding-x); font-size: var(--btn-font-size); font-weight: var(--font-weight-medium); line-height: 1; border-radius: var(--btn-radius); border: var(--btn-border-width) solid transparent; cursor: pointer; -webkit-user-select: none; user-select: none; white-space: nowrap; transition: all var(--transition-fast); outline: none; } .halo-btn:focus-visible { box-shadow: 0 0 0 var(--focus-ring-offset) var(--background-primary), 0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) var(--focus-ring-color); } .halo-btn:disabled, .halo-btn.disabled { opacity: 0.5; pointer-events: none; cursor: not-allowed; } /* Button Variants */ .halo-btn-primary { background: var(--halo-primary); color: #ffffff; border-color: var(--halo-primary); } .halo-btn-primary:hover { background: var(--halo-primary-hover); border-color: var(--halo-primary-hover); } .halo-btn-primary:active { background: var(--halo-primary-active); border-color: var(--halo-primary-active); transform: translateY(1px); } .halo-btn-secondary { background: var(--background-secondary); color: var(--text-primary); border-color: var(--border-color); } .halo-btn-secondary:hover { background: var(--background-tertiary); border-color: var(--border-hover); } .halo-btn-secondary:active { background: var(--background-secondary); transform: translateY(1px); } .halo-btn-danger { background: var(--halo-danger); color: #ffffff; border-color: var(--halo-danger); } .halo-btn-danger:hover { background: var(--halo-danger-hover); border-color: var(--halo-danger-hover); } .halo-btn-danger:active { background: #b91c1c; border-color: #b91c1c; transform: translateY(1px); } .halo-btn-ghost { background: transparent; color: var(--text-primary); border-color: transparent; } .halo-btn-ghost:hover { background: var(--background-secondary); } .halo-btn-ghost:active { background: var(--background-tertiary); } /* Button Sizes */ .halo-btn-sm { --btn-height: 28px; --btn-padding-x: 12px; --btn-font-size: var(--font-size-sm); } .halo-btn-lg { --btn-height: 44px; --btn-padding-x: 24px; --btn-font-size: var(--font-size-lg); } .halo-btn-icon { --btn-height: 36px; --btn-width: 36px; --btn-padding-x: 0; width: var(--btn-width); padding: 0; } .halo-btn-icon.halo-btn-sm { --btn-height: 28px; --btn-width: 28px; } .halo-btn-icon.halo-btn-lg { --btn-height: 44px; --btn-width: 44px; } /* Button Loading State */ .halo-btn.loading { position: relative; pointer-events: none; } .halo-btn.loading::after { content: ''; position: absolute; width: 14px; height: 14px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: halo-spin 0.6s linear infinite; } @keyframes halo-spin { to { transform: rotate(360deg); } } /* --- Card --- */ .halo-card { --card-padding: var(--space-4); --card-radius: var(--radius-lg); --card-shadow: var(--shadow-sm); --card-border: 1px solid var(--border-color); padding: var(--card-padding); border-radius: var(--card-radius); box-shadow: var(--card-shadow); border: var(--card-border); background: var(--background-primary); transition: all var(--transition-normal); } .halo-card-interactive { cursor: pointer; } .halo-card-interactive:hover { box-shadow: var(--shadow-md); border-color: var(--interactive-accent); transform: translateY(-2px); } .halo-card-interactive:active { transform: translateY(0); box-shadow: var(--shadow-sm); } /* Card Parts */ .halo-card-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border-color); margin-bottom: var(--space-3); } .halo-card-title { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin: 0; } .halo-card-content { flex: 1; } .halo-card-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--border-color); margin-top: var(--space-3); } /* --- Input --- */ .halo-input { --input-height: 36px; --input-padding: 8px 12px; --input-radius: var(--radius-md); --input-font-size: var(--font-size-base); --input-border-color: var(--border-color); --input-border-hover: var(--border-hover); --input-border-focus: var(--interactive-accent); display: flex; align-items: center; height: var(--input-height); padding: var(--input-padding); font-size: var(--input-font-size); background: var(--background-primary); border: 1px solid var(--input-border-color); border-radius: var(--input-radius); color: var(--text-primary); transition: all var(--transition-fast); outline: none; } .halo-input:hover { border-color: var(--input-border-hover); } .halo-input:focus { border-color: var(--input-border-focus); box-shadow: 0 0 0 3px var(--halo-primary-muted); } .halo-input::placeholder { color: var(--text-muted); } .halo-input:disabled { opacity: 0.5; cursor: not-allowed; background: var(--background-secondary); } .halo-input.error { border-color: var(--halo-danger); } .halo-input.error:focus { box-shadow: 0 0 0 3px var(--halo-danger-muted); } /* Input with icon */ .halo-input-wrapper { position: relative; display: flex; align-items: center; } .halo-input-wrapper .halo-input { flex: 1; } .halo-input-wrapper .halo-input-icon { position: absolute; left: 12px; color: var(--text-muted); pointer-events: none; } .halo-input-wrapper .halo-input:not(.halo-input-error) { padding-left: 36px; } /* --- Textarea --- */ .halo-textarea { --textarea-min-height: 80px; --textarea-padding: var(--input-padding); min-height: var(--textarea-min-height); padding: var(--textarea-padding); resize: vertical; font-family: inherit; font-size: var(--input-font-size); line-height: 1.5; background: var(--background-primary); border: 1px solid var(--border-color); border-radius: var(--input-radius); color: var(--text-primary); transition: all var(--transition-fast); outline: none; } .halo-textarea:hover { border-color: var(--border-hover); } .halo-textarea:focus { border-color: var(--interactive-accent); box-shadow: 0 0 0 3px var(--halo-primary-muted); } /* --- Select --- */ .halo-select { --select-height: var(--input-height); --select-padding: var(--input-padding); --select-radius: var(--input-radius); appearance: none; height: var(--select-height); padding: var(--select-padding); padding-right: 36px; font-size: var(--input-font-size); background: var(--background-primary) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E") no-repeat right 10px center; border: 1px solid var(--border-color); border-radius: var(--select-radius); color: var(--text-primary); cursor: pointer; transition: all var(--transition-fast); outline: none; } .halo-select:hover { border-color: var(--border-hover); } .halo-select:focus { border-color: var(--interactive-accent); box-shadow: 0 0 0 3px var(--halo-primary-muted); } /* --- Badge --- */ .halo-badge { --badge-padding: 4px 10px; --badge-font-size: var(--font-size-xs); --badge-radius: var(--radius-full); display: inline-flex; align-items: center; gap: 4px; padding: var(--badge-padding); font-size: var(--badge-font-size); font-weight: var(--font-weight-medium); border-radius: var(--badge-radius); line-height: 1; transition: all var(--transition-fast); } .halo-badge-default { background: var(--background-secondary); color: var(--text-secondary); } .halo-badge-primary { background: var(--halo-primary-muted); color: var(--halo-primary); } .halo-badge-success { background: var(--halo-success-muted); color: var(--halo-success); } .halo-badge-warning { background: var(--halo-warning-muted); color: var(--halo-warning); } .halo-badge-danger { background: var(--halo-danger-muted); color: var(--halo-danger); } /* --- Table --- */ .halo-table { --table-border-color: var(--border-color); --table-row-hover: var(--background-secondary); --table-row-selected: var(--halo-primary-muted); width: 100%; border-collapse: collapse; font-size: var(--font-size-sm); } .halo-table th, .halo-table td { padding: var(--space-3) var(--space-4); text-align: left; border-bottom: 1px solid var(--table-border-color); } .halo-table th { font-weight: var(--font-weight-semibold); color: var(--text-secondary); background: var(--background-secondary); } .halo-table tr:hover td { background: var(--table-row-hover); } .halo-table tr.selected td { background: var(--table-row-selected); } /* --- Modal --- */ .halo-modal { --modal-padding: var(--space-6); --modal-radius: var(--radius-xl); --modal-max-width: 520px; position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.5); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); } .halo-modal-content { width: 100%; max-width: var(--modal-max-width); max-height: 90vh; overflow-y: auto; padding: var(--modal-padding); background: var(--background-primary); border-radius: var(--modal-radius); box-shadow: var(--shadow-xl); } .halo-modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-4); } .halo-modal-title { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); margin: 0; } .halo-modal-close { --btn-size: 32px; display: flex; align-items: center; justify-content: center; width: var(--btn-size); height: var(--btn-size); background: transparent; border: none; border-radius: var(--radius-md); color: var(--text-muted); cursor: pointer; transition: all var(--transition-fast); } .halo-modal-close:hover { background: var(--background-secondary); color: var(--text-primary); } .halo-modal-body { margin-bottom: var(--space-4); } .halo-modal-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); padding-top: var(--space-4); border-top: 1px solid var(--border-color); } /* --- Form Group --- */ .halo-form-group { margin-bottom: var(--space-4); } .halo-form-label { display: block; margin-bottom: var(--space-2); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--text-secondary); } .halo-form-helper { margin-top: var(--space-1); font-size: var(--font-size-xs); color: var(--text-muted); } .halo-form-error { margin-top: var(--space-1); font-size: var(--font-size-xs); color: var(--halo-danger); } /* --- Color Picker --- */ .halo-color-picker { display: flex; align-items: center; gap: var(--space-2); } .halo-color-preview { width: 32px; height: 32px; border-radius: var(--radius-md); border: 2px solid var(--border-color); cursor: pointer; transition: all var(--transition-fast); } .halo-color-preview:hover { border-color: var(--interactive-accent); transform: scale(1.05); } .halo-color-input { flex: 1; font-family: monospace; } /* --- Empty State --- */ .halo-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-8); text-align: center; color: var(--text-muted); } .halo-empty-icon { font-size: 48px; margin-bottom: var(--space-4); opacity: 0.5; } .halo-empty-title { font-size: var(--font-size-lg); font-weight: var(--font-weight-medium); margin-bottom: var(--space-2); } .halo-empty-description { font-size: var(--font-size-sm); max-width: 300px; } /* --- Loading Spinner --- */ .halo-spinner { width: 24px; height: 24px; border: 2px solid var(--border-color); border-top-color: var(--interactive-accent); border-radius: 50%; animation: halo-spin 0.6s linear infinite; } .halo-spinner-sm { width: 16px; height: 16px; } .halo-spinner-lg { width: 32px; height: 32px; } /* ============================================ Sync Status View Specific ============================================ */ .sync-header { display: flex; justify-content: space-between; align-items: center; } .sync-view-title { margin: 0; font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); } .sync-actions { display: flex; gap: var(--space-2); } .sync-icon-btn { --btn-size: 36px; display: flex; align-items: center; justify-content: center; width: var(--btn-size); height: var(--btn-size); background: transparent; border: none; border-radius: var(--radius-md); cursor: pointer; font-size: 18px; opacity: 0.7; transition: all var(--transition-fast); } .sync-icon-btn:hover { opacity: 1; background: var(--background-secondary); } .sync-search-bar { padding: var(--space-3) var(--space-4); } .sync-search-input { width: 100%; height: 40px; padding: 0 var(--space-4); font-size: var(--font-size-base); background: var(--background-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-lg); color: var(--text-primary); transition: all var(--transition-fast); outline: none; } .sync-search-input:hover { border-color: var(--border-hover); } .sync-search-input:focus { border-color: var(--interactive-accent); box-shadow: 0 0 0 3px var(--halo-primary-muted); } .sync-filter-tabs { display: flex; padding: 0 var(--space-4); gap: var(--space-1); border-bottom: 1px solid var(--border-color); } .sync-filter-tab { padding: var(--space-3) var(--space-4); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--text-muted); background: transparent; border: none; border-bottom: 2px solid transparent; cursor: pointer; transition: all var(--transition-fast); margin-bottom: -1px; } .sync-filter-tab:hover { color: var(--text-primary); } .sync-filter-tab.active { color: var(--interactive-accent); border-bottom-color: var(--interactive-accent); } .sync-content { flex: 1; overflow-y: auto; padding: var(--space-4); } .sync-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-8); text-align: center; color: var(--text-muted); } .sync-group-header { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) 0; margin-bottom: var(--space-2); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--text-muted); cursor: pointer; transition: color var(--transition-fast); } .sync-group-header:hover { color: var(--interactive-accent); } /* Post Card */ .sync-post-card { background: var(--background-primary); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-3); transition: all var(--transition-normal); } .sync-post-card:hover { border-color: var(--interactive-accent); box-shadow: var(--shadow-md); transform: translateY(-2px); } .card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: var(--space-3); } .card-title-section { display: flex; align-items: center; gap: var(--space-3); flex: 1; min-width: 0; } .sync-post-checkbox { width: 18px; height: 18px; cursor: pointer; accent-color: var(--halo-primary); } .card-status-icon { font-size: 18px; flex-shrink: 0; } .card-title { font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .card-status-badge { flex-shrink: 0; } .card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--font-size-xs); color: var(--text-muted); margin-bottom: var(--space-3); } .card-meta-sep { color: var(--border-color); } .card-tags { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-bottom: var(--space-3); } .card-tag { padding: 2px 8px; font-size: var(--font-size-xs); background: var(--background-secondary); border-radius: var(--radius-sm); color: var(--text-secondary); } .card-tag-more { font-size: var(--font-size-xs); color: var(--text-muted); } .card-actions { display: flex; gap: var(--space-2); } .card-btn { --btn-size: 32px; display: flex; align-items: center; justify-content: center; width: var(--btn-size); height: var(--btn-size); background: var(--background-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-md); font-size: 14px; cursor: pointer; color: var(--text-secondary); transition: all var(--transition-fast); } .card-btn:hover { background: var(--background-tertiary); border-color: var(--border-hover); color: var(--text-primary); } .card-btn-primary { background: var(--halo-primary); border-color: var(--halo-primary); color: #ffffff; } .card-btn-primary:hover { background: var(--halo-primary-hover); border-color: var(--halo-primary-hover); } /* Stats Bar */ .sync-stats { display: flex; justify-content: space-between; align-items: center; padding: var(--space-3) var(--space-4); background: var(--background-secondary); border-top: 1px solid var(--border-color); font-size: var(--font-size-xs); color: var(--text-muted); } .sync-stats-pending { color: var(--halo-warning); } /* History Modal */ .history-modal-title { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); margin: 0 0 var(--space-4) 0; } .history-stats { font-size: var(--font-size-sm); color: var(--text-muted); margin-bottom: var(--space-4); } .history-empty { text-align: center; color: var(--text-muted); padding: var(--space-6); } .history-item { display: flex; justify-content: space-between; align-items: center; padding: var(--space-3); border-bottom: 1px solid var(--border-color); } .history-item:last-child { border-bottom: none; } .history-info { display: flex; align-items: center; gap: var(--space-2); } .history-title { font-size: var(--font-size-sm); } .history-time { font-size: var(--font-size-xs); color: var(--text-muted); } .history-clear-btn { width: 100%; margin-top: var(--space-4); } /* Detail Modal */ .detail-status-badge { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); font-size: var(--font-size-sm); background: var(--background-secondary); border-radius: var(--radius-full); margin-bottom: var(--space-4); } .detail-info { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-4); } .detail-row { display: flex; justify-content: space-between; align-items: center; padding: var(--space-2) 0; } .detail-label { font-size: var(--font-size-sm); color: var(--text-muted); } .detail-value { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); } .detail-version-section { background: var(--background-secondary); padding: var(--space-4); border-radius: var(--radius-lg); margin-bottom: var(--space-4); } .detail-version-title { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-2); } .detail-version-info { font-size: var(--font-size-xs); color: var(--text-muted); } .detail-close-btn { width: 100%; margin-top: var(--space-4); } /* Tag/Category Manager Styles */ .tag-manager-actions, .category-manager-actions { display: flex; justify-content: space-between; align-items: center; padding: var(--space-4); border-bottom: 1px solid var(--border-color); } .tag-list, .category-list { padding: var(--space-4); } .tag-table, .category-table { width: 100%; border-collapse: collapse; } .tag-table th, .tag-table td, .category-table th, .category-table td { padding: var(--space-3) var(--space-4); text-align: left; border-bottom: 1px solid var(--border-color); } .tag-table th, .category-table th { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--text-secondary); background: var(--background-secondary); } .tag-table tr:hover td, .category-table tr:hover td { background: var(--background-secondary); } .tag-color-preview { display: inline-block; width: 24px; height: 24px; border-radius: var(--radius-sm); border: 1px solid var(--border-color); vertical-align: middle; } .tag-action-btn, .category-action-btn { padding: var(--space-1) var(--space-3); margin-right: var(--space-2); font-size: var(--font-size-xs); background: var(--background-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-sm); color: var(--text-secondary); cursor: pointer; transition: all var(--transition-fast); } .tag-action-btn:hover, .category-action-btn:hover { background: var(--background-tertiary); border-color: var(--border-hover); color: var(--text-primary); } .tag-action-btn.danger, .category-action-btn.danger { color: var(--halo-danger); } .tag-action-btn.danger:hover, .category-action-btn.danger:hover { background: var(--halo-danger-muted); border-color: var(--halo-danger); } /* Status Dot Animation */ @keyframes halo-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .halo-status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--halo-success); } .halo-status-dot.pulse { animation: halo-pulse 2s ease-in-out infinite; } .halo-status-dot.warning { background: var(--halo-warning); } .halo-status-dot.danger { background: var(--halo-danger); } /* Skeleton Loading */ @keyframes halo-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } .halo-skeleton { background: linear-gradient( 90deg, var(--background-secondary) 25%, var(--background-tertiary) 50%, var(--background-secondary) 75% ); background-size: 200% 100%; animation: halo-shimmer 1.5s ease-in-out infinite; border-radius: var(--radius-sm); } .halo-skeleton-text { height: 14px; margin-bottom: var(--space-2); } .halo-skeleton-text:last-child { width: 60%; } /* Responsive */ .is-mobile .sync-post-card { border-radius: var(--radius-md); } .is-mobile .halo-modal-content { margin: var(--space-4); max-width: calc(100% - var(--space-8)); }