feat(student-portal): 完整实现 student-portal 微前端
包含 src 全部实现、Dockerfile、配置文件、contracts 包等
This commit is contained in:
196
apps/student-portal/src/styles/globals.css
Normal file
196
apps/student-portal/src/styles/globals.css
Normal file
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* globals.css — student-portal 全局样式(ai14)
|
||||
*
|
||||
* 引入顺序:semantic.css(含 primitive.css)→ Tailwind 三层
|
||||
* 对齐 teacher-portal Shell 纸感设计(paper/ink/accent/rule)
|
||||
*/
|
||||
|
||||
@import './semantic.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* === 基础排版 === */
|
||||
@layer base {
|
||||
html,
|
||||
body {
|
||||
background: var(--color-paper);
|
||||
color: var(--color-ink);
|
||||
font-family: var(--font-sans);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-serif);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
/* 焦点可见性(A11y WCAG 2.2)*/
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* 链接 */
|
||||
a {
|
||||
color: var(--color-accent);
|
||||
text-decoration: none;
|
||||
transition: color var(--duration-fast) var(--ease-standard);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-accent-hover);
|
||||
}
|
||||
}
|
||||
|
||||
/* === 组件层 === */
|
||||
@layer components {
|
||||
/* 纸感分隔线 */
|
||||
.rule {
|
||||
border-top: 1px solid var(--color-rule);
|
||||
}
|
||||
|
||||
.rule-thin {
|
||||
border-top: 2px solid var(--color-rule);
|
||||
}
|
||||
|
||||
/* 左侧竖线标记(节点展开样式,对齐 teacher-portal)*/
|
||||
.mark-left {
|
||||
border-left: 2px solid var(--color-rule);
|
||||
padding-left: var(--space-md);
|
||||
}
|
||||
|
||||
/* 纸感卡片 */
|
||||
.card-paper {
|
||||
background: var(--color-paper);
|
||||
border: 1px solid var(--color-rule);
|
||||
border-radius: var(--radius-base);
|
||||
box-shadow: var(--shadow-paper);
|
||||
}
|
||||
|
||||
/* 主按钮 */
|
||||
.btn-primary {
|
||||
background: var(--color-accent);
|
||||
color: var(--color-paper);
|
||||
padding: var(--space-sm) var(--space-lg);
|
||||
border-radius: var(--radius-base);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--font-size-3);
|
||||
transition: background var(--duration-fast) var(--ease-standard);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--color-accent-hover);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 次级按钮 */
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--color-accent);
|
||||
border: 1px solid var(--color-accent);
|
||||
padding: var(--space-sm) var(--space-lg);
|
||||
border-radius: var(--radius-base);
|
||||
}
|
||||
|
||||
/* 输入框 */
|
||||
.input-paper {
|
||||
background: var(--color-paper);
|
||||
border: 1px solid var(--color-rule);
|
||||
border-radius: var(--radius-base);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--font-size-3);
|
||||
color: var(--color-ink);
|
||||
transition: border-color var(--duration-fast) var(--ease-standard);
|
||||
}
|
||||
|
||||
.input-paper:focus {
|
||||
border-color: var(--color-accent);
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-focus);
|
||||
}
|
||||
|
||||
/* 徽标 */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px var(--space-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--font-size-2);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: hsl(var(--hue-success), var(--sat-success), 92%);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: hsl(var(--hue-warning), var(--sat-warning), 92%);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: hsl(var(--hue-danger), var(--sat-danger), 92%);
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
background: hsl(var(--hue-info), var(--sat-info), 92%);
|
||||
color: var(--color-info);
|
||||
}
|
||||
}
|
||||
|
||||
/* === 工具层 === */
|
||||
@layer utilities {
|
||||
/* 文本截断 */
|
||||
.text-truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 多行截断 */
|
||||
.text-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 屏幕阅读器专用 */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* 减少动画(A11y prefers-reduced-motion)*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.motion-safe {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
apps/student-portal/src/styles/primitive.css
Normal file
89
apps/student-portal/src/styles/primitive.css
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Layer 1 — Primitive 原始令牌(ai14)
|
||||
*
|
||||
* 项目规则 §3.10:原始色板/字号/间距/阴影,业务代码不直接引用
|
||||
* 仅 semantic.css 引用此文件
|
||||
* 与 teacher-portal Shell 令牌对齐(paper/ink/accent/rule)
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* === 原始色板(HSL 分量,供 semantic.css 组合)=== */
|
||||
--hue-paper: 40;
|
||||
--sat-paper: 20%;
|
||||
--light-paper: 98%;
|
||||
|
||||
--hue-ink: 25;
|
||||
--sat-ink: 3%;
|
||||
--light-ink: 15%;
|
||||
--light-ink-muted: 45%;
|
||||
--light-ink-subtle: 65%;
|
||||
|
||||
--hue-accent: 220;
|
||||
--sat-accent: 60%;
|
||||
--light-accent: 35%;
|
||||
--light-accent-hover: 28%;
|
||||
--light-accent-muted: 92%;
|
||||
|
||||
--hue-rule: 30;
|
||||
--sat-rule: 10%;
|
||||
--light-rule: 90%;
|
||||
|
||||
/* 语义色相 */
|
||||
--hue-success: 142;
|
||||
--sat-success: 50%;
|
||||
--light-success: 35%;
|
||||
|
||||
--hue-warning: 38;
|
||||
--sat-warning: 80%;
|
||||
--light-warning: 45%;
|
||||
|
||||
--hue-danger: 0;
|
||||
--sat-danger: 70%;
|
||||
--light-danger: 45%;
|
||||
|
||||
--hue-info: 200;
|
||||
--sat-info: 70%;
|
||||
--light-info: 45%;
|
||||
|
||||
/* === 字号阶梯(对齐 var(--font-size-1~9))=== */
|
||||
--font-size-1: 12px;
|
||||
--font-size-2: 14px;
|
||||
--font-size-3: 16px;
|
||||
--font-size-4: 18px;
|
||||
--font-size-5: 20px;
|
||||
--font-size-6: 24px;
|
||||
--font-size-7: 30px;
|
||||
--font-size-8: 36px;
|
||||
--font-size-9: 48px;
|
||||
|
||||
/* === 间距阶梯 === */
|
||||
--space-xs: 4px;
|
||||
--space-sm: 8px;
|
||||
--space-md: 12px;
|
||||
--space-lg: 16px;
|
||||
--space-xl: 24px;
|
||||
--space-2xl: 32px;
|
||||
--space-3xl: 48px;
|
||||
|
||||
/* === 圆角 === */
|
||||
--radius-sm: 3px;
|
||||
--radius-base: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
|
||||
/* === 阴影(纸感)=== */
|
||||
--shadow-paper: 0 1px 2px hsl(25, 3%, 15%, 0.04);
|
||||
--shadow-card: 0 2px 8px hsl(25, 3%, 15%, 0.06);
|
||||
--shadow-focus: 0 0 0 3px hsl(220, 60%, 35%, 0.2);
|
||||
|
||||
/* === 字体族(仅 primitive.css 可定义字面量)=== */
|
||||
--font-serif-literal: 'Fraunces', Georgia, serif;
|
||||
--font-sans-literal: 'Inter', system-ui, sans-serif;
|
||||
--font-mono-literal: 'JetBrains Mono', monospace;
|
||||
|
||||
/* === 过渡 === */
|
||||
--duration-fast: 120ms;
|
||||
--duration-base: 200ms;
|
||||
--duration-slow: 400ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
}
|
||||
58
apps/student-portal/src/styles/semantic.css
Normal file
58
apps/student-portal/src/styles/semantic.css
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Layer 2 — Semantic 语义令牌(ai14)
|
||||
*
|
||||
* 项目规则 §3.10:业务代码唯一引用入口
|
||||
* 引用 primitive.css 原始令牌组合为语义令牌
|
||||
* 支持 light/dark 双主题(当前仅 light)
|
||||
*/
|
||||
|
||||
@import './primitive.css';
|
||||
|
||||
:root {
|
||||
/* === 语义色(light 主题)=== */
|
||||
--color-paper: hsl(var(--hue-paper), var(--sat-paper), var(--light-paper));
|
||||
--color-ink: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink));
|
||||
--color-ink-muted: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink-muted));
|
||||
--color-ink-subtle: hsl(var(--hue-ink), var(--sat-ink), var(--light-ink-subtle));
|
||||
|
||||
--color-accent: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent));
|
||||
--color-accent-hover: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent-hover));
|
||||
--color-accent-muted: hsl(var(--hue-accent), var(--sat-accent), var(--light-accent-muted));
|
||||
|
||||
--color-rule: hsl(var(--hue-rule), var(--sat-rule), var(--light-rule));
|
||||
|
||||
--color-success: hsl(var(--hue-success), var(--sat-success), var(--light-success));
|
||||
--color-warning: hsl(var(--hue-warning), var(--sat-warning), var(--light-warning));
|
||||
--color-danger: hsl(var(--hue-danger), var(--sat-danger), var(--light-danger));
|
||||
--color-info: hsl(var(--hue-info), var(--sat-info), var(--light-info));
|
||||
|
||||
/* === 语义字体族(暴露给 Tailwind)=== */
|
||||
--font-serif: var(--font-serif-literal);
|
||||
--font-sans: var(--font-sans-literal);
|
||||
--font-mono: var(--font-mono-literal);
|
||||
|
||||
/* === 语义间距 === */
|
||||
--space-xs: var(--space-xs);
|
||||
--space-sm: var(--space-sm);
|
||||
--space-md: var(--space-md);
|
||||
--space-lg: var(--space-lg);
|
||||
--space-xl: var(--space-xl);
|
||||
--space-2xl: var(--space-2xl);
|
||||
--space-3xl: var(--space-3xl);
|
||||
}
|
||||
|
||||
/* dark 主题预留(P6+ 启用)*/
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme='light']) {
|
||||
--color-paper: hsl(var(--hue-paper), var(--sat-paper), 8%);
|
||||
--color-ink: hsl(var(--hue-ink), var(--sat-ink), 92%);
|
||||
--color-ink-muted: hsl(var(--hue-ink), var(--sat-ink), 65%);
|
||||
--color-ink-subtle: hsl(var(--hue-ink), var(--sat-ink), 45%);
|
||||
|
||||
--color-accent: hsl(var(--hue-accent), var(--sat-accent), 55%);
|
||||
--color-accent-hover: hsl(var(--hue-accent), var(--sat-accent), 65%);
|
||||
--color-accent-muted: hsl(var(--hue-accent), var(--sat-accent), 15%);
|
||||
|
||||
--color-rule: hsl(var(--hue-rule), var(--sat-rule), 20%);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user