divan/GanjooRazor/wwwroot/css/user-panel.css
Hamid Reza Mohammadi 96a36f2dc2 editor improvements
2026-09-05 16:08:00 +03:30

844 lines
18 KiB
CSS

/*
User area (پیشخان کاربر) design foundation.
Scoped, additive layer on top of p8.css/site.css — nothing here overrides
the public site's look. Tokens are pulled from p8.css's existing palette
(brand maroon #300, success/danger greens & reds, 5px radius, existing
box-shadow recipe) so the panel still feels like Ganjoor, just tidier.
Class prefix "up-" (user-panel) is used throughout to avoid collisions
with the large, unscoped p8.css class list.
*/
:root {
--up-primary: #300;
--up-primary-hover: #4d1010;
--up-on-primary: #fff;
--up-success: #0a0;
--up-success-dark: darkgreen;
--up-danger: #f00;
--up-danger-dark: darkred;
--up-gray: slategray;
--up-bg: #f5f7fb;
--up-surface: #fff;
--up-surface-alt: #f4f1ec;
--up-border: #e0e0e0;
--up-text: #222;
--up-text-muted: #666;
--up-radius-sm: 5px;
--up-radius: 10px;
--up-radius-lg: 16px;
--up-shadow: 0 5px 0.3rem rgba(0, 0, 0, 0.3);
--up-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.12);
--up-space-1: 4px;
--up-space-2: 8px;
--up-space-3: 16px;
--up-space-4: 24px;
--up-space-5: 32px;
--up-font: 'Vazirmatn', sans-serif;
}
@media (prefers-color-scheme: dark) {
:root {
--up-bg: #121212;
--up-surface: #1c1c1c;
--up-surface-alt: #232323;
--up-border: #3a3a3a;
--up-text: bisque;
--up-text-muted: #c9b8a8;
--up-primary: #a54a4a;
--up-primary-hover: #c25e5e;
--up-on-primary: #fff;
}
}
/* ---------- layout shell ---------- */
.up-shell {
font-family: var(--up-font);
color: var(--up-text);
text-align: right;
max-width: 760px;
margin: 0 auto;
}
/* opt-out for pages that are genuinely wide data grids (Poets, Donations,
Banners, LongRunningJobs, Expenses) - set via ViewData["WideShell"] */
.up-shell--wide {
max-width: none;
}
.up-page-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--up-space-2);
margin-bottom: var(--up-space-3);
flex-wrap: wrap;
}
.up-page-header h1 {
margin: 0;
font-size: 1.5rem;
}
.up-page-subtitle {
color: var(--up-text-muted);
font-size: 0.9rem;
margin-top: var(--up-space-1);
}
/* ---------- card ---------- */
.up-card {
background: var(--up-surface);
border: 1px solid var(--up-border);
border-radius: var(--up-radius-lg);
box-shadow: var(--up-shadow-sm);
padding: var(--up-space-4);
margin-bottom: var(--up-space-3);
}
.up-card + .up-card {
margin-top: var(--up-space-3);
}
.up-card__title {
margin: 0 0 var(--up-space-3);
font-size: 1.1rem;
border-bottom: 1px solid var(--up-border);
padding-bottom: var(--up-space-2);
}
/* ---------- buttons ---------- */
/* Compatible in spirit with the existing .pagebutton/.editor-button look
(radius, shadow) so mixed usage during the migration doesn't feel jarring. */
.up-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--up-space-1);
cursor: pointer;
font-family: var(--up-font);
font-size: 0.9rem;
padding: 8px 16px;
border-radius: var(--up-radius-sm);
border: 1px solid transparent;
box-shadow: var(--up-shadow);
color: var(--up-on-primary);
background-color: var(--up-primary);
text-decoration: none;
transition: background-color 0.15s ease, opacity 0.15s ease, transform 0.05s ease;
}
/* p8.css (site-wide, pre-existing) separately defines .up-btn::after as part of an
unrelated arrow_circle_up/down/left/right icon family for vote-style controls.
That class name collides with ours - cancel its injected icon here since
user-panel.css loads after p8.css and wins the cascade at equal specificity. */
.up-btn::after {
content: none;
}
.up-btn:hover {
background-color: var(--up-primary-hover);
color: var(--up-on-primary);
text-decoration: none;
}
.up-btn:active {
transform: translateY(1px);
}
.up-btn:disabled,
.up-btn[aria-disabled="true"] {
opacity: 0.55;
cursor: not-allowed;
transform: none;
}
.up-btn--success {
background-color: var(--up-success-dark);
}
.up-btn--success:hover {
background-color: var(--up-success);
}
.up-btn--danger {
background-color: var(--up-danger-dark);
}
.up-btn--danger:hover {
background-color: var(--up-danger);
}
.up-btn--ghost {
background-color: transparent;
color: var(--up-text);
border-color: var(--up-border);
box-shadow: none;
}
.up-btn--ghost:hover {
background-color: var(--up-surface-alt);
color: var(--up-text);
}
.up-btn--sm {
padding: 4px 10px;
font-size: 0.8rem;
}
.up-btn--block {
display: flex;
width: 100%;
}
.up-btn-group {
display: flex;
gap: var(--up-space-2);
flex-wrap: wrap;
}
.up-btn-group + .up-btn-group,
.up-btn-group + .up-btn-group-label {
margin-top: var(--up-space-3);
}
.up-btn-group-label {
font-size: 0.72rem;
color: var(--up-text-muted);
margin-bottom: var(--up-space-1);
}
.up-btn-group-label:first-child {
margin-top: 0;
}
/* icon-only action buttons (delete/mark-read/etc.) */
.up-icon-btn {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--up-border);
background: var(--up-surface);
color: var(--up-text);
}
.up-icon-btn:hover {
background: var(--up-surface-alt);
}
/* ---------- alerts / notices ---------- */
.up-alert {
border-radius: var(--up-radius);
padding: var(--up-space-3);
margin-bottom: var(--up-space-3);
border: 1px solid transparent;
font-size: 0.9rem;
}
.up-alert--info {
background: #eef3fb;
border-color: #cfe0f5;
color: #1c3a5e;
}
.up-alert--success {
background: #eaf7ea;
border-color: #c9e8c9;
color: #1e4620;
}
.up-alert--danger {
background: #fbeaea;
border-color: #f0caca;
color: #5e1c1c;
}
.up-alert--warning {
background: #fff6e5;
border-color: #f3ddaa;
color: #5e4a1c;
}
@media (prefers-color-scheme: dark) {
.up-alert--info { background: #172633; border-color: #24405c; color: bisque; }
.up-alert--success { background: #142a16; border-color: #1f4a22; color: bisque; }
.up-alert--danger { background: #331616; border-color: #5c2424; color: bisque; }
.up-alert--warning { background: #332a14; border-color: #5c4a1f; color: bisque; }
}
/* ---------- badges ---------- */
.up-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 999px;
font-size: 0.75rem;
line-height: 1.6;
background: var(--up-surface-alt);
color: var(--up-text-muted);
border: 1px solid var(--up-border);
}
.up-badge--primary { background: var(--up-primary); color: var(--up-on-primary); border-color: transparent; }
.up-badge--success { background: var(--up-success-dark); color: #fff; border-color: transparent; }
.up-badge--danger { background: var(--up-danger-dark); color: #fff; border-color: transparent; }
/* ---------- empty state ---------- */
.up-empty {
text-align: center;
padding: var(--up-space-5) var(--up-space-3);
color: var(--up-text-muted);
}
.up-empty__icon {
font-size: 2rem;
margin-bottom: var(--up-space-2);
opacity: 0.6;
}
/* ---------- pagination ---------- */
.up-pagination {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: var(--up-space-1);
margin: var(--up-space-3) 0;
}
.up-pagination a,
.up-pagination span {
display: inline-flex;
min-width: 32px;
height: 32px;
padding: 0 8px;
align-items: center;
justify-content: center;
border-radius: var(--up-radius-sm);
font-size: 0.85rem;
text-decoration: none;
color: var(--up-text);
}
.up-pagination a {
border: 1px solid var(--up-border);
background: var(--up-surface);
}
.up-pagination a:hover {
background: var(--up-surface-alt);
text-decoration: none;
}
.up-pagination span.up-pagination__current {
background: var(--up-primary);
color: var(--up-on-primary);
font-weight: 600;
}
/* ---------- list items (notifications, comments, edits...) ---------- */
.up-list {
display: flex;
flex-direction: column;
gap: var(--up-space-2);
}
.up-list-item {
background: var(--up-surface);
border: 1px solid var(--up-border);
border-radius: var(--up-radius);
padding: var(--up-space-3);
position: relative;
}
.up-list-item--unread {
border-inline-start: 4px solid var(--up-primary);
}
.up-list-item__meta {
color: var(--up-text-muted);
font-size: 0.8rem;
margin-inline-start: var(--up-space-2);
}
.up-list-item__actions {
display: flex;
gap: var(--up-space-2);
margin-top: var(--up-space-2);
}
/* ---------- toasts ---------- */
.up-toast-container {
position: fixed;
bottom: var(--up-space-4);
left: 50%;
transform: translateX(-50%);
z-index: 1080;
display: flex;
flex-direction: column;
gap: var(--up-space-2);
align-items: center;
pointer-events: none;
width: 100%;
padding: 0 var(--up-space-3);
}
.up-toast {
pointer-events: auto;
width: max-content;
max-width: min(420px, 90vw);
padding: 10px 20px;
border-radius: var(--up-radius);
box-shadow: var(--up-shadow-sm);
color: #fff;
background: var(--up-gray);
font-size: 0.85rem;
opacity: 0;
transform: translateY(8px);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.up-toast--visible {
opacity: 1;
transform: translateY(0);
}
.up-toast--success {
background: var(--up-success-dark);
}
.up-toast--error {
background: var(--up-danger-dark);
}
/* ---------- confirm modal (replaces window.confirm) ---------- */
.up-modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1090;
padding: var(--up-space-3);
}
.up-modal-backdrop[hidden] {
display: none;
}
.up-modal {
background: var(--up-surface);
border-radius: var(--up-radius-lg);
box-shadow: var(--up-shadow-sm);
padding: var(--up-space-4);
max-width: 400px;
width: 100%;
text-align: center;
}
.up-modal__message {
margin: 0 0 var(--up-space-4);
font-size: 0.95rem;
line-height: 1.6;
}
.up-modal__actions {
display: flex;
gap: var(--up-space-2);
justify-content: center;
}
.up-modal__actions .up-btn {
flex: 1;
}
body.up-modal-open {
overflow: hidden;
}
/* ---------- dashboard quick links ---------- */
.up-quick-links {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: var(--up-space-3);
margin-bottom: var(--up-space-4);
}
.up-quick-link {
display: flex;
flex-direction: column;
gap: var(--up-space-1);
text-decoration: none;
color: var(--up-text);
transition: transform 0.1s ease, box-shadow 0.15s ease;
}
.up-quick-link:hover {
text-decoration: none;
color: var(--up-text);
box-shadow: var(--up-shadow);
transform: translateY(-2px);
}
/* ---------- moderation review-result radio options / generic checkbox rows ---------- */
.up-radio-option,
.up-check-row {
display: flex;
align-items: center;
gap: var(--up-space-1);
padding: 3px 0;
}
.up-radio-option label,
.up-check-row label {
margin: 0;
cursor: pointer;
}
.up-verse-row {
padding: 8px 0;
border-bottom: 1px solid var(--up-border);
line-height: 1.9;
}
.up-verse-row:last-child {
border-bottom: none;
}
.up-verse-row .up-field {
line-height: 1.5;
margin-bottom: 6px;
}
/* ---------- small utilities ---------- */
.up-mt-0 { margin-top: 0; }
.up-text-muted { color: var(--up-text-muted); }
.up-text-center { text-align: center; }
/* ---------- forms ---------- */
.up-form {
display: flex;
flex-direction: column;
gap: var(--up-space-3);
}
.up-field {
display: flex;
flex-direction: column;
gap: 2px;
}
.up-field > label {
font-size: 0.72rem;
line-height: 1.3;
color: var(--up-text-muted);
display: flex;
align-items: center;
gap: 4px;
}
.up-field > label .up-field-icon {
font-size: 0.85em;
opacity: 0.8;
}
.up-field-row {
display: flex;
gap: var(--up-space-3);
flex-wrap: wrap;
}
.up-field-row > .up-field {
flex: 1 1 160px;
min-width: 140px;
}
.up-field input[type="text"],
.up-field input[type="password"],
.up-field input[type="email"],
.up-field input:not([type]),
.up-field textarea,
.up-field select {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--up-border);
border-radius: var(--up-radius-sm);
background: var(--up-surface);
color: var(--up-text);
font-family: var(--up-font);
font-size: 0.9rem;
}
.up-field select {
width: auto;
max-width: 100%;
cursor: pointer;
}
.up-field textarea {
min-height: 120px;
resize: vertical;
}
.up-field input:focus,
.up-field textarea:focus,
.up-field select:focus {
outline: none;
border-color: var(--up-primary);
box-shadow: 0 0 0 3px rgba(51, 0, 0, 0.12);
}
/* ---------- editable surfaces (contenteditable divs standing in for inputs) ----------
Needs a strong, unmistakable affordance since it isn't a native form control -
nothing else on the page should look like this. */
.up-editable {
display: block;
position: relative;
width: 100%;
min-height: 1.6em;
padding: 9px 34px 9px 12px;
border: 1px solid var(--up-primary);
border-radius: var(--up-radius-sm);
background: var(--up-surface-alt);
color: var(--up-text);
font-family: var(--up-font);
font-size: 0.9rem;
cursor: text;
transition: box-shadow .15s ease, border-color .15s ease, background .15s ease;
}
.up-editable::before {
content: "\270E";
position: absolute;
top: 8px;
left: 10px;
font-size: 0.85em;
opacity: 0.55;
pointer-events: none;
}
.up-editable:hover {
border-color: var(--up-primary);
box-shadow: 0 0 0 2px rgba(51, 0, 0, 0.08);
}
.up-editable:focus {
outline: none;
background: var(--up-surface);
border-color: var(--up-primary);
box-shadow: 0 0 0 3px rgba(51, 0, 0, 0.16);
}
@media (prefers-color-scheme: dark) {
.up-editable:hover {
box-shadow: 0 0 0 2px rgba(165, 74, 74, 0.18);
}
.up-editable:focus {
box-shadow: 0 0 0 3px rgba(165, 74, 74, 0.3);
}
}
/* read-only display text: kept deliberately plain/quiet so it never competes
visually with .up-editable / real form controls */
.up-field-static {
background: none;
border: none;
}
@media (prefers-color-scheme: dark) {
.up-field input:focus,
.up-field textarea:focus {
box-shadow: 0 0 0 3px rgba(165, 74, 74, 0.25);
}
}
.up-field--ltr input,
.up-field--ltr textarea,
.up-field--ltr .up-field-static {
direction: ltr;
text-align: left;
}
.up-field-static {
padding: 2px 0;
color: var(--up-text-muted);
font-size: 0.9rem;
}
/* ---------- progressive disclosure ----------
Hidden by default (inline style="display:none" set in markup); JS reveals
it only once the editable content actually diverges from the original,
so read-only/diff information doesn't compete for attention until it's
relevant. */
.up-reveal {
margin-top: var(--up-space-2);
padding-top: var(--up-space-2);
border-top: 1px dashed var(--up-border);
}
.up-hint {
font-size: 0.78rem;
color: var(--up-text-muted);
line-height: 1.6;
}
.up-hint--danger {
color: var(--up-danger-dark);
}
.up-verse-number {
align-items: center;
justify-content: center;
min-width: 26px;
height: 26px;
padding: 0 8px;
border-radius: 999px;
background: var(--up-primary);
color: var(--up-on-primary);
font-weight: 600;
font-size: 0.85rem;
line-height: 1;
margin-inline-end: var(--up-space-2);
vertical-align: middle;
}
.up-form-actions {
display: flex;
gap: var(--up-space-2);
flex-wrap: wrap;
}
/* ---------- danger zone (destructive account actions) ---------- */
.up-danger-zone {
border-color: var(--up-danger-dark);
}
.up-danger-zone .up-card__title {
color: var(--up-danger-dark);
border-bottom-color: var(--up-danger-dark);
}
/* ---------- navbar dropdowns ---------- */
/* Bootstrap 4's default (non-RTL) build anchors dropdown-menu to the left
and left-aligns text, which reads wrong on this RTL page. */
.navbar .dropdown-menu {
right: 0;
left: auto;
text-align: right;
border-radius: var(--up-radius);
border-color: var(--up-border);
box-shadow: var(--up-shadow-sm);
}
.navbar .dropdown-item {
text-align: right;
}
.navbar .up-badge {
margin-inline-start: 6px;
vertical-align: middle;
font-size: 0.65rem;
padding: 1px 8px;
}
.navbar form {
margin-bottom: 0;
}
.navbar form button.nav-link {
background: none;
border: none;
padding: 0.5rem 1rem;
font-size: 1rem;
line-height: inherit;
cursor: pointer;
}
.navbar form button.nav-link:hover,
.navbar form button.nav-link:focus {
color: var(--up-link-hover, #0056b3);
text-decoration: none;
}
/* ---------- role chips (Users.cshtml) ---------- */
.up-role-chip-row {
display: flex;
align-items: center;
gap: var(--up-space-2);
flex-wrap: wrap;
padding: var(--up-space-2) 0;
}
.up-role-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: 999px;
background: var(--up-surface-alt);
border: 1px solid var(--up-border);
font-size: 0.85rem;
}
.up-role-chip__remove {
cursor: pointer;
color: var(--up-text-muted);
font-size: 0.75rem;
}
.up-role-chip__remove:hover {
color: var(--up-danger, #c0392b);
}
/* ---------- permission tree (Roles.cshtml) ---------- */
.up-perm-tree {
text-align: right;
margin-bottom: var(--up-space-3);
}
.up-perm-tree__item {
margin-top: var(--up-space-3);
}
.up-perm-tree__item:first-child {
margin-top: 0;
}
.up-perm-tree__ops {
display: flex;
flex-wrap: wrap;
gap: var(--up-space-3);
margin-top: var(--up-space-1);
padding-right: var(--up-space-3);
}