feat(workbench): 全面优化个案工作台页面体验
性能与加载: - 分阶段加载:档案头、个案数据、档案数据各自独立加载,骨架屏替代 spinner - PatientHeadSkeleton / WorkbenchSkeleton / ArchiveSkeleton 三套骨架 - 档案库懒加载(切换 Tab 时才请求) 交互增强: - Alert 卡片增加「标记已读」和「关闭」按钮,调用 acknowledgeAlert / resolveAlert API - 态势感知 Detail 区支持同时展开多个类型(状态独立) - StatCard 点击可折叠/展开,带 chevron 指示 - 待办列表增加分页(每页 8 条) AI 建议: - 从单一方案扩展为 3 套方案(保守 A / 常规 B / 积极 C) - 支持切换标签,卡片内动画过渡 - 每套方案可一键生成对应处置单 - 预置空腹血糖、血压、体重等多套模板 指标卡片: - Sparkline 增加阈值参考虚线(min/max) - 超限高亮点 + 「超限」角标 - 档案库扩展为 8 个指标卡(新增舒张压、OGTT 1h/2h、餐后血糖) - 阈值常量统一管理(THRESHOLDS Map) 分页: - 档案库观测历史分页(每页 8 条) - 待办列表分页 样式完善: - badge-emerald / badge-rose / badge-indigo / badge-calm - btn-xs / btn-success / btn-rose - grid-4 / align-center / border-l-* - 脉冲危急点动画、Tab 未读计数角标 - Alert 支持 updatedAt,severity 别名兼容后端字段 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,10 @@ export const api = {
|
|||||||
listObservations: (patientId: string): Promise<Observation[]> =>
|
listObservations: (patientId: string): Promise<Observation[]> =>
|
||||||
request(`/patients/${patientId}/observations`),
|
request(`/patients/${patientId}/observations`),
|
||||||
listAlerts: (patientId: string): Promise<Alert[]> => request(`/patients/${patientId}/alerts`),
|
listAlerts: (patientId: string): Promise<Alert[]> => request(`/patients/${patientId}/alerts`),
|
||||||
|
acknowledgeAlert: (patientId: string, alertId: string): Promise<Alert> =>
|
||||||
|
request(`/patients/${patientId}/alerts/${alertId}/acknowledge`, { method: 'POST' }),
|
||||||
|
resolveAlert: (patientId: string, alertId: string): Promise<Alert> =>
|
||||||
|
request(`/patients/${patientId}/alerts/${alertId}/resolve`, { method: 'POST' }),
|
||||||
listReminders: (patientId: string): Promise<Reminder[]> =>
|
listReminders: (patientId: string): Promise<Reminder[]> =>
|
||||||
request(`/patients/${patientId}/reminders`),
|
request(`/patients/${patientId}/reminders`),
|
||||||
|
|
||||||
|
|||||||
@@ -64,10 +64,13 @@ export interface Alert {
|
|||||||
indicator: string;
|
indicator: string;
|
||||||
value: number;
|
value: number;
|
||||||
level: RiskLevel;
|
level: RiskLevel;
|
||||||
|
/** severity 为 level 的别名,后端字段对齐 */
|
||||||
|
severity: RiskLevel;
|
||||||
ruleIds: string[];
|
ruleIds: string[];
|
||||||
messages: string[];
|
messages: string[];
|
||||||
status: AlertStatus;
|
status: AlertStatus;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 个案
|
// 个案
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* 骨架屏动画 */
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { background-position: -400px 0; }
|
||||||
|
100% { background-position: 400px 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line {
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--color-border) 25%,
|
||||||
|
var(--color-bg) 50%,
|
||||||
|
var(--color-border) 75%
|
||||||
|
);
|
||||||
|
background-size: 800px 100%;
|
||||||
|
animation: shimmer 1.5s infinite linear;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-root {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
/**
|
||||||
|
* 骨架屏组件 — 真实布局骨架,提升感知加载速度
|
||||||
|
* 替代整页 loading spinner,让用户"看到"即将呈现的内容结构
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './Skeleton.css';
|
||||||
|
|
||||||
|
interface SkeletonLineProps {
|
||||||
|
width?: string;
|
||||||
|
height?: string;
|
||||||
|
radius?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SkeletonLine({ width = '100%', height = '14px', radius = '4px' }: SkeletonLineProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="skeleton-line"
|
||||||
|
style={{ width, height, borderRadius: radius }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SkeletonBlock({ width = '100%', height = '80px' }: { width?: string; height?: string }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="skeleton-line"
|
||||||
|
style={{ width, height, borderRadius: '8px' }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SkeletonCircle({ size = '48px' }: { size?: string }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="skeleton-line"
|
||||||
|
style={{ width: size, height: size, borderRadius: '50%' }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PatientHeadSkeleton(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="card patient-head skeleton-root">
|
||||||
|
<div className="patient-head__main">
|
||||||
|
<SkeletonCircle size="48px" />
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginBottom: '8px', alignItems: 'center' }}>
|
||||||
|
<SkeletonLine width="120px" height="18px" />
|
||||||
|
<SkeletonLine width="60px" height="18px" radius="12px" />
|
||||||
|
<SkeletonLine width="60px" height="18px" radius="12px" />
|
||||||
|
</div>
|
||||||
|
<SkeletonLine width="280px" height="12px" />
|
||||||
|
<div style={{ marginTop: '6px' }}>
|
||||||
|
<SkeletonLine width="200px" height="10px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WorkbenchSkeleton(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="stack">
|
||||||
|
{/* Tab 骨架 */}
|
||||||
|
<div className="skeleton-root" style={{ display: 'flex', gap: '4px', padding: '4px', borderRadius: '8px', background: 'var(--color-bg)', border: '1px solid var(--color-border)' }}>
|
||||||
|
<SkeletonLine width="160px" height="36px" radius="6px" />
|
||||||
|
<SkeletonLine width="180px" height="36px" radius="6px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 态势感知双栏 */}
|
||||||
|
<div className="workbench-layout-container">
|
||||||
|
<div className="card shadow-sm workbench-master skeleton-root">
|
||||||
|
<SkeletonLine width="80px" height="14px" />
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', marginTop: '12px' }}>
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', padding: '8px 12px', border: '1px solid var(--color-border)', borderRadius: '6px' }}>
|
||||||
|
<SkeletonLine width="80px" height="12px" />
|
||||||
|
<SkeletonLine width="40px" height="20px" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card shadow-sm workbench-detail skeleton-root">
|
||||||
|
<SkeletonLine width="100px" height="14px" />
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginTop: '12px' }}>
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} style={{ padding: '8px 12px', border: '1px solid var(--color-border)', borderRadius: '6px' }}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '6px' }}>
|
||||||
|
<SkeletonLine width="120px" height="12px" />
|
||||||
|
<SkeletonLine width="40px" height="18px" radius="10px" />
|
||||||
|
</div>
|
||||||
|
<SkeletonLine width="200px" height="10px" />
|
||||||
|
<SkeletonLine width="160px" height="10px" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 流程卡片 */}
|
||||||
|
<div className="card skeleton-root">
|
||||||
|
<SkeletonLine width="80px" height="14px" />
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginTop: '12px', flexWrap: 'wrap' }}>
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
||||||
|
<SkeletonLine key={i} width="60px" height="28px" radius="14px" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<SkeletonLine width="200px" height="12px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 指标卡片网格 */}
|
||||||
|
<div className="grid-4" style={{ gap: 'var(--space-3)' }}>
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="card skeleton-root">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||||
|
<SkeletonLine width="70px" height="12px" />
|
||||||
|
<SkeletonLine width="40px" height="18px" radius="8px" />
|
||||||
|
</div>
|
||||||
|
<SkeletonLine width="100px" height="24px" />
|
||||||
|
<div style={{ marginTop: '12px' }}>
|
||||||
|
<SkeletonLine width="160px" height="40px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 双栏 */}
|
||||||
|
<div className="grid-2 workbench-grid">
|
||||||
|
<div className="card skeleton-root">
|
||||||
|
<SkeletonLine width="120px" height="14px" />
|
||||||
|
<SkeletonBlock height="120px" />
|
||||||
|
</div>
|
||||||
|
<div className="card skeleton-root">
|
||||||
|
<SkeletonLine width="120px" height="14px" />
|
||||||
|
<SkeletonBlock height="120px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArchiveSkeleton(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="stack">
|
||||||
|
<div className="grid-4">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="card skeleton-root">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||||
|
<SkeletonLine width="80px" height="12px" />
|
||||||
|
<SkeletonLine width="36px" height="16px" radius="8px" />
|
||||||
|
</div>
|
||||||
|
<SkeletonLine width="60px" height="22px" />
|
||||||
|
<SkeletonLine width="160px" height="36px" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="grid-2">
|
||||||
|
<div className="card skeleton-root">
|
||||||
|
<SkeletonLine width="140px" height="14px" />
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px', marginTop: '12px' }}>
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div key={i}>
|
||||||
|
<SkeletonLine width="60px" height="10px" />
|
||||||
|
<SkeletonLine width="80px" height="12px" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card skeleton-root">
|
||||||
|
<SkeletonLine width="180px" height="14px" />
|
||||||
|
<SkeletonBlock height="100px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -592,8 +592,118 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AI 建议多方案切换 */
|
||||||
|
.ai-variant-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: var(--space-3);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.ai-variant-tab {
|
||||||
|
padding: 5px 12px;
|
||||||
|
border: 1.5px solid var(--color-border-strong);
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: var(--color-bg);
|
||||||
|
font-size: var(--font-xs);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
.ai-variant-tab:hover {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary-strong);
|
||||||
|
background: var(--color-primary-soft);
|
||||||
|
}
|
||||||
|
.ai-variant-tab.active {
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
box-shadow: 0 2px 8px rgba(220, 86, 138, 0.3);
|
||||||
|
}
|
||||||
|
.ai-variant-detail {
|
||||||
|
animation: fadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页 */
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-top: var(--space-3);
|
||||||
|
font-size: var(--font-xs);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
.pagination button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 脉冲危急点 */
|
||||||
|
@keyframes pulse-alert {
|
||||||
|
0%, 100% { opacity: 1; transform: scale(1); }
|
||||||
|
50% { opacity: 0.6; transform: scale(0.85); }
|
||||||
|
}
|
||||||
|
.pulse-dot {
|
||||||
|
animation: pulse-alert 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 告警卡操作按钮 */
|
||||||
|
.alert-item-card .btn-xs {
|
||||||
|
padding: 2px 4px;
|
||||||
|
min-width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
.alert-item-card .btn-xs:hover {
|
||||||
|
color: var(--color-primary-strong);
|
||||||
|
background: var(--color-primary-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 舒张压卡片单独显示 */
|
||||||
|
.diagostic-indicator-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab badge */
|
||||||
|
.tab-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
background: var(--color-danger);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 9px;
|
||||||
|
animation: pulse-alert 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 细化 detail-section 动画 */
|
||||||
|
.detail-section {
|
||||||
|
animation: fadeIn 0.2s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dispose-status badge 扩展 */
|
||||||
|
.disp-status-badge.pending_confirmation { background: rgba(201,130,31,0.12); color: var(--color-warn); border: 1px solid rgba(201,130,31,0.3); padding: 2px 8px; border-radius: var(--radius-sm); font-size: var(--font-xs); font-weight: 700; }
|
||||||
|
.disp-status-badge.executing { background: rgba(47,111,176,0.1); color: var(--color-calm); border: 1px solid rgba(47,111,176,0.3); padding: 2px 8px; border-radius: var(--radius-sm); font-size: var(--font-xs); font-weight: 700; }
|
||||||
|
.disp-status-badge.closed { background: var(--color-ok-soft); color: var(--color-ok); border: 1px solid rgba(46,158,107,0.3); padding: 2px 8px; border-radius: var(--radius-sm); font-size: var(--font-xs); font-weight: 700; }
|
||||||
|
|
||||||
|
/* justify-center */
|
||||||
|
.justify-center { justify-content: center; }
|
||||||
|
|
||||||
@media (max-width: 980px) {
|
@media (max-width: 980px) {
|
||||||
.workbench-grid {
|
.grid-4 { grid-template-columns: repeat(2, 1fr) !important; }
|
||||||
grid-template-columns: 1fr;
|
.indicators-summary-grid { grid-template-columns: repeat(2, 1fr) !important; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -100,6 +100,19 @@ h3 {
|
|||||||
.btn-block {
|
.btn-block {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.btn-xs {
|
||||||
|
padding: 3px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
.btn-success {
|
||||||
|
background: var(--color-ok);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.btn-rose {
|
||||||
|
background: var(--color-danger);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
.btn:disabled {
|
.btn:disabled {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@@ -187,6 +200,22 @@ h3 {
|
|||||||
background: var(--color-calm-soft);
|
background: var(--color-calm-soft);
|
||||||
color: var(--color-calm);
|
color: var(--color-calm);
|
||||||
}
|
}
|
||||||
|
.badge-emerald {
|
||||||
|
background: var(--color-calm-soft);
|
||||||
|
color: var(--color-calm);
|
||||||
|
}
|
||||||
|
.badge-rose {
|
||||||
|
background: var(--color-danger-soft);
|
||||||
|
color: var(--color-danger);
|
||||||
|
}
|
||||||
|
.badge-indigo {
|
||||||
|
background: var(--color-primary-soft);
|
||||||
|
color: var(--color-primary-strong);
|
||||||
|
}
|
||||||
|
.badge-calm {
|
||||||
|
background: var(--color-calm-soft);
|
||||||
|
color: var(--color-calm);
|
||||||
|
}
|
||||||
|
|
||||||
/* 辅助 */
|
/* 辅助 */
|
||||||
.muted {
|
.muted {
|
||||||
@@ -212,6 +241,14 @@ h3 {
|
|||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
|
.grid-4 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
.align-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.empty {
|
.empty {
|
||||||
color: var(--color-text-soft);
|
color: var(--color-text-soft);
|
||||||
font-size: var(--font-sm);
|
font-size: var(--font-sm);
|
||||||
@@ -247,3 +284,49 @@ h3 {
|
|||||||
.pager .btn-sm {
|
.pager .btn-sm {
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 实用工具 */
|
||||||
|
.text-right { text-align: right; }
|
||||||
|
.shadow-sm { box-shadow: 0 1px 3px rgba(31, 39, 51, 0.08); }
|
||||||
|
.shadow { box-shadow: var(--shadow-card); }
|
||||||
|
.text-center { text-align: center; }
|
||||||
|
.text-left { text-align: left; }
|
||||||
|
.border-l-indigo { border-left: 3px solid var(--color-primary); }
|
||||||
|
.border-l-emerald { border-left: 3px solid var(--color-calm); }
|
||||||
|
.border-l-rose { border-left: 3px solid var(--color-danger); }
|
||||||
|
.bg-light-gradient { background: linear-gradient(135deg, #fff 0%, #faf7fc 100%); }
|
||||||
|
.bg-light { background: var(--color-surface-2); }
|
||||||
|
.shadow-pop { box-shadow: var(--shadow-pop); }
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(31, 39, 51, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.modal-panel {
|
||||||
|
background: var(--color-surface);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: var(--space-6);
|
||||||
|
min-width: 360px;
|
||||||
|
max-width: 560px;
|
||||||
|
box-shadow: var(--shadow-pop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input variants */
|
||||||
|
.textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 9px 12px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--color-border-strong);
|
||||||
|
background: var(--color-surface);
|
||||||
|
resize: vertical;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: var(--font-md);
|
||||||
|
}
|
||||||
|
.textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-calm);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user