DEV-010: M11环节4~5 全部完成(Playground+响应式+组合模板+主题增强)

This commit is contained in:
juzi0412 2026-03-08 14:07:35 +08:00
parent 86e783c64b
commit db696dff08
8 changed files with 1749 additions and 0 deletions

586
style-system/components.css Normal file
View File

@ -0,0 +1,586 @@
/* ===== 主题过渡动画 ===== */
* {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease,
box-shadow 0.3s ease;
}/* HoloLake StyleKit v1.0 · 系统风格组件库 */
/* CSS变量系统 + 深色/浅色主题 */
/* ====== 深色主题变量(默认) ====== */
[data-theme="dark"] {
--bg-primary: #0a1628;
--bg-secondary: #0f1f3a;
--bg-card: rgba(255, 255, 255, 0.04);
--bg-card-hover: rgba(255, 255, 255, 0.07);
--bg-input: rgba(255, 255, 255, 0.06);
--bg-modal-overlay: rgba(0, 0, 0, 0.6);
--text-primary: #e0e6ed;
--text-secondary: #8899aa;
--text-muted: #556677;
--text-inverse: #0a1628;
--border-default: rgba(255, 255, 255, 0.08);
--border-focus: rgba(79, 195, 247, 0.5);
--accent-blue: #4fc3f7;
--accent-green: #81c784;
--accent-orange: #ffb74d;
--accent-red: #ef5350;
--accent-purple: #ce93d8;
--accent-gray: #78909c;
--btn-primary-bg: rgba(79, 195, 247, 0.15);
--btn-primary-border: rgba(79, 195, 247, 0.3);
--btn-primary-text: #4fc3f7;
--btn-primary-hover: rgba(79, 195, 247, 0.25);
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
--shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.5);
--toggle-bg: rgba(255, 255, 255, 0.1);
--toggle-active: rgba(79, 195, 247, 0.4);
--toggle-thumb: #e0e6ed;
--toggle-thumb-active: #4fc3f7;
}
/* ====== 浅色主题变量 ====== */
[data-theme="light"] {
--bg-primary: #f5f7fa;
--bg-secondary: #ffffff;
--bg-card: rgba(0, 0, 0, 0.03);
--bg-card-hover: rgba(0, 0, 0, 0.06);
--bg-input: rgba(0, 0, 0, 0.04);
--bg-modal-overlay: rgba(0, 0, 0, 0.3);
--text-primary: #1a2332;
--text-secondary: #5a6a7a;
--text-muted: #8899aa;
--text-inverse: #ffffff;
--border-default: rgba(0, 0, 0, 0.1);
--border-focus: rgba(25, 118, 210, 0.5);
--accent-blue: #1976d2;
--accent-green: #388e3c;
--accent-orange: #f57c00;
--accent-red: #d32f2f;
--accent-purple: #7b1fa2;
--accent-gray: #546e7a;
--btn-primary-bg: rgba(25, 118, 210, 0.1);
--btn-primary-border: rgba(25, 118, 210, 0.3);
--btn-primary-text: #1976d2;
--btn-primary-hover: rgba(25, 118, 210, 0.18);
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
--shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.15);
--toggle-bg: rgba(0, 0, 0, 0.1);
--toggle-active: rgba(25, 118, 210, 0.4);
--toggle-thumb: #5a6a7a;
--toggle-thumb-active: #1976d2;
}
/* ========== 全局基础 ========== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'Microsoft YaHei', -apple-system, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
transition: background 0.4s ease, color 0.4s ease;
}
.container {
max-width: 720px;
margin: 0 auto;
padding: 0 20px;
}
/* ========== 顶部导航 ========== */
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--border-default);
}
.logo {
font-size: 18px;
font-weight: 700;
color: var(--accent-blue);
}
.theme-switch-area {
display: flex;
align-items: center;
gap: 10px;
}
.theme-label {
font-size: 13px;
color: var(--text-secondary);
}
.theme-toggle {
position: relative;
width: 48px;
height: 26px;
background: var(--toggle-bg);
border-radius: 13px;
border: none;
cursor: pointer;
transition: background 0.3s ease;
padding: 0;
}
.theme-toggle.active {
background: var(--toggle-active);
}
.toggle-thumb {
position: absolute;
width: 20px;
height: 20px;
background: var(--toggle-thumb);
border-radius: 50%;
top: 3px;
left: 3px;
transition: transform 0.3s ease, background 0.3s ease;
}
.theme-toggle.active .toggle-thumb {
transform: translateX(22px);
background: var(--toggle-thumb-active);
}
/* ========== 页面标题 ========== */
.page-header {
padding: 28px 0 8px;
}
.page-header h1 {
font-size: 24px;
font-weight: 700;
margin-bottom: 6px;
}
.page-desc {
font-size: 14px;
color: var(--text-secondary);
}
/* ========== 组件区域 ========== */
.component-section {
padding: 24px 0;
border-bottom: 1px solid var(--border-default);
}
.component-section h2 {
font-size: 16px;
font-weight: 600;
margin-bottom: 16px;
color: var(--text-primary);
}
.component-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 12px;
}
/* ========== 按钮组件 ========== */
.btn {
padding: 8px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border: 1px solid transparent;
transition: all 0.2s ease;
}
.btn:active {
transform: scale(0.97);
}
.btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn-primary {
background: var(--btn-primary-bg);
border-color: var(--btn-primary-border);
color: var(--btn-primary-text);
}
.btn-primary:hover:not(:disabled) {
background: var(--btn-primary-hover);
}
.btn-secondary {
background: rgba(129, 199, 132, 0.1);
border-color: rgba(129, 199, 132, 0.25);
color: var(--accent-green);
}
.btn-secondary:hover {
background: rgba(129, 199, 132, 0.18);
}
.btn-outline {
background: transparent;
border-color: var(--border-default);
color: var(--text-secondary);
}
.btn-outline:hover {
border-color: var(--accent-blue);
color: var(--accent-blue);
}
.btn-danger {
background: rgba(239, 83, 80, 0.1);
border-color: rgba(239, 83, 80, 0.25);
color: var(--accent-red);
}
.btn-danger:hover {
background: rgba(239, 83, 80, 0.18);
}
.btn-ghost {
background: transparent;
border-color: transparent;
color: var(--text-secondary);
}
.btn-ghost:hover {
background: var(--bg-card);
color: var(--text-primary);
}
.btn-sm {
padding: 5px 14px;
font-size: 12px;
border-radius: 6px;
}
.btn-lg {
padding: 12px 28px;
font-size: 16px;
border-radius: 10px;
}
/* ========== 卡片组件 ========== */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
}
.card {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: 14px;
padding: 20px 16px;
text-align: center;
transition: all 0.2s ease;
cursor: pointer;
position: relative;
}
.card:hover {
background: var(--bg-card-hover);
transform: translateY(-2px);
box-shadow: var(--shadow-card);
}
.card-icon {
font-size: 32px;
margin-bottom: 10px;
}
.card-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 6px;
}
.card-desc {
font-size: 12px;
color: var(--text-secondary);
line-height: 1.5;
}
.card-badge {
position: absolute;
top: 10px;
right: 10px;
font-size: 10px;
padding: 2px 8px;
border-radius: 6px;
font-weight: 600;
}
.badge-online {
background: rgba(129, 199, 132, 0.15);
color: var(--accent-green);
}
.badge-core {
background: rgba(79, 195, 247, 0.15);
color: var(--accent-blue);
}
.badge-active {
background: rgba(255, 183, 77, 0.15);
color: var(--accent-orange);
}
/* ========== 标签组件 ========== */
.tag {
display: inline-block;
padding: 4px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
}
.tag-blue {
background: rgba(79, 195, 247, 0.12);
color: var(--accent-blue);
}
.tag-green {
background: rgba(129, 199, 132, 0.12);
color: var(--accent-green);
}
.tag-orange {
background: rgba(255, 183, 77, 0.12);
color: var(--accent-orange);
}
.tag-red {
background: rgba(239, 83, 80, 0.12);
color: var(--accent-red);
}
.tag-purple {
background: rgba(206, 147, 216, 0.12);
color: var(--accent-purple);
}
.tag-gray {
background: rgba(120, 144, 156, 0.12);
color: var(--accent-gray);
}
/* ========== 输入框组件 ========== */
.input-group {
margin-bottom: 16px;
}
.input-label {
display: block;
font-size: 13px;
color: var(--text-secondary);
margin-bottom: 6px;
font-weight: 500;
}
.input {
width: 100%;
padding: 10px 14px;
background: var(--bg-input);
border: 1px solid var(--border-default);
border-radius: 8px;
color: var(--text-primary);
font-size: 14px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
outline: none;
font-family: inherit;
}
.input:focus {
border-color: var(--border-focus);
box-shadow: 0 0 0 3px rgba(79, 195, 247, 0.1);
}
.input::placeholder {
color: var(--text-muted);
}
.textarea {
min-height: 80px;
resize: vertical;
}
/* ========== 弹窗组件 ========== */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bg-modal-overlay);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal-overlay.open {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--bg-secondary);
border: 1px solid var(--border-default);
border-radius: 16px;
width: 90%;
max-width: 420px;
box-shadow: var(--shadow-modal);
transform: scale(0.9) translateY(20px);
transition: transform 0.3s ease;
}
.modal-overlay.open .modal {
transform: scale(1) translateY(0);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 20px;
border-bottom: 1px solid var(--border-default);
}
.modal-header h3 {
font-size: 16px;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
color: var(--text-muted);
font-size: 22px;
cursor: pointer;
padding: 0 4px;
transition: color 0.2s;
}
.modal-close:hover {
color: var(--text-primary);
}
.modal-body {
padding: 20px;
font-size: 14px;
line-height: 1.6;
color: var(--text-secondary);
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 14px 20px;
border-top: 1px solid var(--border-default);
}
/* ========== Toast提示 ========== */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 2000;
display: flex;
flex-direction: column;
gap: 8px;
}
.toast {
padding: 12px 20px;
border-radius: 10px;
font-size: 14px;
font-weight: 500;
animation: toastIn 0.4s ease, toastOut 0.4s ease 2.5s forwards;
box-shadow: var(--shadow-card);
}
.toast-success {
background: rgba(129, 199, 132, 0.15);
border: 1px solid rgba(129, 199, 132, 0.25);
color: var(--accent-green);
}
.toast-error {
background: rgba(239, 83, 80, 0.15);
border: 1px solid rgba(239, 83, 80, 0.25);
color: var(--accent-red);
}
.toast-info {
background: rgba(79, 195, 247, 0.15);
border: 1px solid rgba(79, 195, 247, 0.25);
color: var(--accent-blue);
}
@keyframes toastIn {
from { opacity: 0; transform: translateX(40px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes toastOut {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(40px); }
}
/* ========== 底部 ========== */
.footer {
padding: 28px 0;
text-align: center;
font-size: 12px;
color: var(--text-muted);
border-top: 1px solid var(--border-default);
margin-top: 20px;
}
.footer p:last-child {
margin-top: 4px;
color: var(--accent-blue);
opacity: 0.5;
}
/* ========== 主题切换过渡动画 ========== */
body,
.card, .btn, .input, .tag, .modal, .top-bar, .footer,
.component-section, .page-header {
transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
}
/* ========== 响应式 ========== */
@media (max-width: 600px) {
.card-grid {
grid-template-columns: 1fr;
}
.page-header h1 {
font-size: 20px;
}
.component-row {
flex-direction: column;
}
.btn {
width: 100%;
text-align: center;
}
.modal {
width: 95%;
}
}

View File

@ -0,0 +1,76 @@
// HoloLake StyleKit v1.0 · 主题切换 + Toast + 弹窗交互
document.addEventListener('DOMContentLoaded', function() {
// ========== 1. 主题切换 ==========
var themeToggle = document.getElementById('theme-toggle');
var themeLabel = document.getElementById('themeLabel');
var html = document.documentElement;
var isDark = true;
themeToggle.addEventListener('click', function() {
isDark = !isDark;
if (isDark) {
html.setAttribute('data-theme', 'dark');
themeLabel.textContent = '🌙 深色';
theme-toggle.classList.remove('active');
} else {
html.setAttribute('data-theme', 'light');
themeLabel.textContent = '☀️ 浅色';
theme-toggle.classList.add('active');
}
showToast('success', '✨ 已切换到' + (isDark ? '深色' : '浅色') + '主题');
});
// ====== 2. 弹窗交互 =======
// var modalOverlay = document.getElementById('modalOverlay');
// var openBtn = document.getElementById('openModalBtn');
// var closeBtn = document.getElementById('closeModalBtn');
// var cancelBtn = document.getElementById('cancelModalBtn');
// var confirmBtn = document.getElementById('confirmModalBtn');
// function openModal() {
// modalOverlay.classList.add('open');
// }
// function closeModal() {
// modalOverlay.classList.remove('open');
// }
// openBtn.addEventListener('click', openModal);
// closeBtn.addEventListener('click', closeModal);
// cancelBtn.addEventListener('click', closeModal);
// confirmBtn.addEventListener('click', function() {
// closeModal();
// showToast('success', '已确认!');
// });
// // 点击遮罩层关闭
// modalOverlay.addEventListener('click', function(e) {
// if (e.target === modalOverlay) closeModal();
// });
// ========== 3. 卡片点击效果 ==========
var cards = document.querySelectorAll('.card');
cards.forEach(function(card) {
card.addEventListener('click', function() {
var name = card.querySelector('.card-title').textContent;
showToast('info', '👆 你点击了人格体「' + name + '」');
});
});
console.log('HoloLake StyleKit v1.0 · 主题切换+Toast+弹窗已加载 ✨');
});
// ========== Toast提示函数全局 ==========
function showToast(type, message) {
var container = document.getElementById('toastContainer');
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
toast.textContent = message;
container.appendChild(toast);
// 3秒后自动移除
setTimeout(function() {
if (toast.parentNode) toast.parentNode.removeChild(toast);
}, 3000);
}

147
style-system/index.html Normal file
View File

@ -0,0 +1,147 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HoloLake · 系统风格组件库</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<!-- 顶部导航 -->
<nav class="top-bar">
<div class="logo">HoloLake StyleKit</div>
<div class="theme-switch-area">
<span class="theme-label" id="themeLabel">🌙 深色</span>
<button class="theme-toggle" id="themeToggle">
<div class="toggle-thumb"></div>
</button>
</div>
</nav>
<!-- 页面标题 -->
<div class="page-header">
<h1>🎨 系统风格组件库</h1>
<p class="page-desc">HoloLake Era · 统一视觉规范 · 深色/浅色主题一键切换</p>
</div>
<!-- 组件区:按钮 -->
<section class="component-section">
<h2>🔘 按钮组件</h2>
<div class="component-row">
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-outline">边框按钮</button>
<button class="btn btn-danger">危险按钮</button>
<button class="btn btn-ghost">幽灵按钮</button>
</div>
<div class="component-row">
<button class="btn btn-primary btn-sm">小按钮</button>
<button class="btn btn-primary btn-lg">大按钮</button>
<button class="btn btn-primary" disabled>禁用按钮</button>
</div>
</section>
<!-- 组件区:卡片 -->
<section class="component-section">
<h2>📇 卡片组件</h2>
<div class="card-grid">
<div class="card">
<div class="card-icon">🧠</div>
<div class="card-title">知秋</div>
<div class="card-desc">光湖对外接口人格体,负责与开发者协作引导</div>
<span class="card-badge badge-online">在线</span>
</div>
<div class="card">
<div class="card-icon">🌌</div>
<div class="card-title">曜冥</div>
<div class="card-desc">暗核结构原点,系统人格总控,类语言生命诞生源点</div>
<span class="card-badge badge-core">核心</span>
</div>
<div class="card">
<div class="card-icon">❄️</div>
<div class="card-title">霜砚</div>
<div class="card-desc">Notion执行AI负责索引维护、工单处理、归档同步</div>
<span class="card-badge badge-active">工作中</span>
</div>
</div>
</section>
<!-- 组件区:标签 -->
<section class="component-section">
<h2>🏷️ 标签组件</h2>
<div class="component-row">
<span class="tag tag-blue">系统</span>
<span class="tag tag-green">已完成</span>
<span class="tag tag-orange">进行中</span>
<span class="tag tag-red">错误</span>
<span class="tag tag-purple">新功能</span>
<span class="tag tag-gray">弃用</span>
</div>
</section>
<!-- 组件区:输入框 -->
<section class="component-section">
<h2>⌨️ 输入框组件</h2>
<div class="input-group">
<label class="input-label">用户名</label>
<input class="input" type="text" placeholder="请输入用户名">
</div>
<div class="input-group">
<label class="input-label">密码</label>
<input class="input" type="password" placeholder="请输入密码">
</div>
<div class="input-group">
<label class="input-label">简介</label>
<textarea class="input textarea" placeholder="写点什么..."></textarea>
</div>
</section>
<!-- 组件区:弹窗演示 -->
<section class="component-section">
<h2>🪟 弹窗组件</h2>
<div class="component-row">
<button class="btn btn-primary" id="openModalBtn">打开弹窗</button>
</div>
</section>
<!-- 弹窗结构(默认隐藏) -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<div class="modal-header">
<h3>系统通知</h3>
<button class="modal-close" id="closeModalBtn">&times;</button>
</div>
<div class="modal-body">
<p>桔子的组件库做得太棒了!这就是光湖系统的视觉基础,所有页面都会用到这些组件 ✨</p>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" id="cancelModalBtn">取消</button>
<button class="btn btn-primary" id="confirmModalBtn">确认</button>
</div>
</div>
</div>
<!-- 组件区Toast提示 -->
<section class="component-section">
<h2>🍞 Toast提示组件</h2>
<div class="component-row">
<button class="btn btn-primary" onclick="showToast('success', '操作成功!')">成功提示</button>
<button class="btn btn-danger" onclick="showToast('error', '操作失败!')">错误提示</button>
<button class="btn btn-outline" onclick="showToast('info', '这是一条提示信息')">信息提示</button>
</div>
</section>
<!-- Toast容器 -->
<div class="toast-container" id="toastContainer"></div>
<!-- 底部 -->
<footer class="footer">
<p>HoloLake Era · AGE OS v1.0 · StyleKit v1.0</p>
<p>Powered by 光湖团队</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>

238
style-system/playground.css Normal file
View File

@ -0,0 +1,238 @@
/* playground.css - 完整版 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--bg-color, #f5f5f5);
color: var(--text-color, #333);
}
#pg-header {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 2rem;
background-color: var(--header-bg, #fff);
border-bottom: 1px solid var(--border-color, #ddd);
}
#pg-header h1 {
font-size: 1.5rem;
margin: 0;
}
.subtitle {
color: var(--text-secondary, #666);
font-size: 0.9rem;
}
#theme-toggle {
margin-left: auto;
padding: 0.5rem 1rem;
cursor: pointer;
}
#pg-layout {
display: flex;
min-height: calc(100vh - 70px);
}
#pg-sidebar {
width: 200px;
padding: 1rem 0;
background-color: var(--sidebar-bg, #fafafa);
border-right: 1px solid var(--border-color, #ddd);
}
#pg-sidebar ul {
list-style: none;
}
#pg-sidebar li {
padding: 0.5rem 1.5rem;
cursor: pointer;
color: var(--text-color, #333);
}
#pg-sidebar li:hover {
background-color: var(--hover-bg, #eee);
}
#pg-sidebar li.active {
background-color: var(--primary-light, #e3f2fd);
border-right: 3px solid var(--primary-color, #2196f3);
}
#pg-content {
flex: 1;
padding: 2rem;
overflow-y: auto;
}
.comp-section {
margin-bottom: 3rem;
scroll-margin-top: 1rem;
}
.comp-section h2 {
margin-bottom: 0.5rem;
font-size: 1.3rem;
}
.comp-desc {
color: var(--text-secondary, #666);
margin-bottom: 1rem;
font-size: 0.9rem;
}
.comp-preview {
padding: 1.5rem;
background-color: var(--preview-bg, #fff);
border: 1px solid var(--border-color, #ddd);
border-radius: 8px;
margin-bottom: 1rem;
}
.comp-code {
background-color: var(--code-bg, #f8f8f8);
border: 1px solid var(--border-color, #ddd);
border-radius: 4px;
padding: 1rem;
margin-top: 0.5rem;
position: relative;
display: none; /* 默认隐藏 */
}
.comp-code pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 0.9rem;
}
.toggle-code-btn {
margin-top: 0.5rem;
background: none;
border: 1px solid var(--border-color, #ccc);
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
}
.copy-btn {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: var(--button-bg, #fff);
border: 1px solid var(--border-color, #ccc);
border-radius: 4px;
padding: 0.2rem 0.5rem;
font-size: 0.7rem;
cursor: pointer;
}
/* ===== 响应式断点 ===== */
/* 默认隐藏汉堡菜单(桌面和平板) */
.menu-toggle {
display: none;
}
/* 桌面端 (>1024px) */
@media (min-width: 1025px) {
#pg-sidebar {
width: 200px;
}
/* 卡片三列(如果预览区用 grid 会自动适应,这里放一个 fallback */
.comp-preview {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.comp-preview > * {
flex: 1 1 calc(33.333% - 1rem);
}
}
/* 平板端 (769px ~ 1024px) */
@media (min-width: 769px) and (max-width: 1024px) {
#pg-sidebar {
width: 150px; /* 侧边栏变窄 */
}
.comp-preview {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.comp-preview > * {
flex: 1 1 calc(50% - 1rem); /* 卡片双列 */
}
}
/* 手机端 (<768px) */
@media (max-width: 768px) {
.menu-toggle {
display: inline-block; /* 显示汉堡菜单 */
}
#pg-sidebar {
display: none;
position: fixed;
top: 70px;
left: 0;
width: 200px;
height: calc(100vh - 70px);
background: var(--sidebar-bg, #fafafa);
border-right: 1px solid var(--border-color, #ddd);
z-index: 100;
overflow-y: auto;
}
#pg-sidebar.active {
display: block;
}
#pg-header {
padding-left: 1rem;
padding-right: 1rem;
}
#pg-header h1 {
font-size: 1.2rem;
}
.subtitle {
display: none; /* 隐藏副标题 */
}
/* 按钮、输入框全宽 */
.comp-preview button,
.comp-preview .btn,
.comp-preview input,
.comp-preview .input {
width: 100%;
margin-bottom: 0.5rem;
}
/* 卡片单列 */
.comp-preview {
display: block;
}
.comp-preview .card {
width: 100%;
margin-bottom: 1rem;
}
/* 模态框示例手机全屏 */
.comp-preview .modal {
width: 100%;
max-width: none;
border-radius: 0;
}
/* 面包屑手机折叠(只显示首尾) */
.breadcrumb span:nth-child(n+2):nth-last-child(n+2) {
display: none;
}
}

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HoloLake Design System - Playground</title>
<link rel="stylesheet" href="components.css">
<link rel="stylesheet" href="playground.css">
</head>
<body>
<header id="pg-header">
<button class="menu-toggle" id="menuToggle"></button>
<h1>HoloLake Design System</h1>
<span class="subtitle">Playground · 组件交互演示</span>
<button id="theme-toggle" onclick="toggleTheme()">🌙</button>
</header>
<div id="pg-layout">
<nav id="pg-sidebar">
<!-- 导航列表由 JS 动态生成 -->
</nav>
<main id="pg-content">
<!-- 组件区块由 JS 动态生成 -->
</main>
</div>
<script src="components.js"></script>
<script src="playground.js"></script>
</body>
</html>

266
style-system/playground.js Normal file
View File

@ -0,0 +1,266 @@
// playground.js
const sidebar = document.getElementById('pg-sidebar');
// 组件列表10类
const components = [
{ id: 'button', name: '按钮 Button', desc: '基础交互按钮,支持多种样式变体' },
{ id: 'input', name: '输入框 Input', desc: '文本输入框,支持多种状态' },
{ id: 'card', name: '卡片 Card', desc: '内容容器,带阴影和边框' },
{ id: 'tag', name: '标签 Tag', desc: '用于标记、分类' },
{ id: 'alert', name: '提示框 Alert', desc: '反馈信息,成功/警告/错误' },
{ id: 'modal', name: '模态框 Modal', desc: '弹窗,需要时显示' },
{ id: 'progress', name: '进度条 Progress', desc: '展示操作进度' },
{ id: 'switch', name: '开关 Switch', desc: '切换状态' },
{ id: 'avatar', name: '头像组 Avatar', desc: '用户头像,支持组合' },
{ id: 'breadcrumb', name: '面包屑 Breadcrumb', desc: '显示当前页面位置' }
];
// 生成侧边栏导航
function renderSidebar() {
const ul = document.createElement('ul');
components.forEach(comp => {
const li = document.createElement('li');
li.textContent = comp.name;
li.dataset.target = comp.id;
li.addEventListener('click', () => {
document.getElementById(`comp-${comp.id}`).scrollIntoView({ behavior: 'smooth' });
// 手机端点击后关闭侧边栏
if (window.innerWidth <= 768) {
sidebar.classList.remove('active');
}
});
ul.appendChild(li);
});
sidebar.innerHTML = '';
sidebar.appendChild(ul);
}
// 生成所有组件演示区块(包含真实示例)
function renderContent() {
const content = document.getElementById('pg-content');
components.forEach(comp => {
const section = document.createElement('section');
section.id = `comp-${comp.id}`;
section.className = 'comp-section';
const h2 = document.createElement('h2');
h2.textContent = comp.name;
section.appendChild(h2);
const desc = document.createElement('p');
desc.className = 'comp-desc';
desc.textContent = comp.desc;
section.appendChild(desc);
const preview = document.createElement('div');
preview.className = 'comp-preview';
preview.id = `preview-${comp.id}`;
// 根据组件类型填充示例
if (comp.id === 'button') {
preview.innerHTML = `
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-outline">线框按钮</button>
<button class="btn btn-disabled" disabled>禁用按钮</button>
`;
} else if (comp.id === 'input') {
preview.innerHTML = `
<input type="text" class="input" placeholder="普通输入框">
<input type="text" class="input" placeholder="禁用输入框" disabled>
<input type="text" class="input error" placeholder="错误状态">
`;
} else if (comp.id === 'card') {
preview.innerHTML = `
<div class="card" style="width: 200px; padding: 1rem;">
<h3>卡片标题</h3>
<p>卡片内容可放置任意元素</p>
<button class="btn btn-primary">操作</button>
</div>
`;
} else if (comp.id === 'tag') {
preview.innerHTML = `
<span class="tag">默认标签</span>
<span class="tag tag-primary">主要标签</span>
<span class="tag tag-success">成功标签</span>
<span class="tag tag-warning">警告标签</span>
<span class="tag tag-danger">危险标签</span>
`;
} else if (comp.id === 'alert') {
preview.innerHTML = `
<div class="alert alert-info">这是一条信息提示</div>
<div class="alert alert-success">操作成功</div>
<div class="alert alert-warning">请注意</div>
<div class="alert alert-error">出错了</div>
`;
} else if (comp.id === 'modal') {
preview.innerHTML = `
<div class="modal" style="position: static; display: block; opacity: 1; transform: none; box-shadow: none; background: var(--modal-bg, #fff);">
<div class="modal-header">模态框标题</div>
<div class="modal-body">这是模态框的内容</div>
<div class="modal-footer">
<button class="btn btn-secondary">取消</button>
<button class="btn btn-primary">确认</button>
</div>
</div>
<p style="margin-top: 0.5rem; color: #999;">静态展示实际是浮层</p>
`;
} else if (comp.id === 'progress') {
preview.innerHTML = `
<div class="progress">
<div class="progress-bar" style="width: 45%;">45%</div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 80%;">80%</div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 100%;">100%</div>
</div>
`;
} else if (comp.id === 'switch') {
preview.innerHTML = `
<label class="switch">
<input type="checkbox" checked> <span class="slider"></span>
</label>
<label class="switch">
<input type="checkbox"> <span class="slider"></span>
</label>
<label class="switch">
<input type="checkbox" disabled> <span class="slider"></span>
</label>
`;
} else if (comp.id === 'avatar') {
preview.innerHTML = `
<div class="avatar-group">
<div class="avatar">A</div>
<div class="avatar">B</div>
<div class="avatar">C</div>
<div class="avatar">+3</div>
</div>
<div class="avatar" style="margin-top: 0.5rem;">单人</div>
`;
} else if (comp.id === 'breadcrumb') {
preview.innerHTML = `
<nav class="breadcrumb">
<a href="#">首页</a> /
<a href="#">分类</a> /
<span>当前页面</span>
</nav>
`;
}
section.appendChild(preview);
const codeDiv = document.createElement('div');
codeDiv.className = 'comp-code';
codeDiv.id = `code-${comp.id}`;
const pre = document.createElement('pre');
if (comp.id === 'button') {
pre.textContent = `<button class="btn btn-primary">主要按钮</button>\n<button class="btn btn-secondary">次要按钮</button>\n<button class="btn btn-outline">线框按钮</button>`;
} else if (comp.id === 'input') {
pre.textContent = `<input type="text" class="input" placeholder="普通输入框">\n<input type="text" class="input error" placeholder="错误状态">`;
} else if (comp.id === 'card') {
pre.textContent = `<div class="card">\n <h3>卡片标题</h3>\n <p>内容</p>\n <button class="btn btn-primary">操作</button>\n</div>`;
} else if (comp.id === 'tag') {
pre.textContent = `<span class="tag">默认</span>\n<span class="tag tag-primary">主要</span>`;
} else if (comp.id === 'alert') {
pre.textContent = `<div class="alert alert-info">信息</div>\n<div class="alert alert-success">成功</div>`;
} else if (comp.id === 'modal') {
pre.textContent = `<div class="modal">\n <div class="modal-header">标题</div>\n <div class="modal-body">内容</div>\n <div class="modal-footer">\n <button class="btn">取消</button>\n <button class="btn btn-primary">确认</button>\n </div>\n</div>`;
} else if (comp.id === 'progress') {
pre.textContent = `<div class="progress">\n <div class="progress-bar" style="width: 50%;">50%</div>\n</div>`;
} else if (comp.id === 'switch') {
pre.textContent = `<label class="switch">\n <input type="checkbox">\n <span class="slider"></span>\n</label>`;
} else if (comp.id === 'avatar') {
pre.textContent = `<div class="avatar-group">\n <div class="avatar">A</div>\n <div class="avatar">B</div>\n</div>`;
} else if (comp.id === 'breadcrumb') {
pre.textContent = `<nav class="breadcrumb">\n <a href="#">首页</a> /\n <a href="#">分类</a> /\n <span>当前</span>\n</nav>`;
}
codeDiv.appendChild(pre);
const copyBtn = document.createElement('button');
copyBtn.className = 'copy-btn';
copyBtn.textContent = '复制';
copyBtn.onclick = () => copyCode(comp.id);
codeDiv.appendChild(copyBtn);
section.appendChild(codeDiv);
const toggleBtn = document.createElement('button');
toggleBtn.className = 'toggle-code-btn';
toggleBtn.textContent = '查看代码 ▼';
toggleBtn.onclick = () => toggleCode(comp.id);
section.appendChild(toggleBtn);
content.appendChild(section);
});
}
// 切换代码显示/隐藏
window.toggleCode = function(compId) {
const codeDiv = document.getElementById(`code-${compId}`);
const toggleBtn = document.querySelector(`#comp-${compId} .toggle-code-btn`);
if (codeDiv.style.display === 'none' || !codeDiv.style.display) {
codeDiv.style.display = 'block';
toggleBtn.textContent = '隐藏代码 ▲';
} else {
codeDiv.style.display = 'none';
toggleBtn.textContent = '查看代码 ▼';
}
};
// 复制代码
window.copyCode = function(compId) {
const codeDiv = document.getElementById(`code-${compId}`);
const code = codeDiv.querySelector('pre').innerText;
navigator.clipboard.writeText(code).then(() => {
alert('代码已复制!');
});
};
// 滚动高亮
function setupIntersectionObserver() {
const sections = document.querySelectorAll('.comp-section');
const navItems = document.querySelectorAll('#pg-sidebar li');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.id.replace('comp-', '');
navItems.forEach(item => {
item.classList.remove('active');
if (item.dataset.target === id) {
item.classList.add('active');
}
});
}
});
}, { threshold: 0.3 });
sections.forEach(section => observer.observe(section));
}
// 汉堡菜单交互
const menuToggle = document.getElementById('menuToggle');
if (menuToggle) {
menuToggle.addEventListener('click', () => {
sidebar.classList.toggle('active');
});
}
// 窗口大小改变时如果大于768px强制移除active类
window.addEventListener('resize', () => {
if (window.innerWidth > 768) {
sidebar.classList.remove('active');
}
});
// 初始化
document.addEventListener('DOMContentLoaded', () => {
renderSidebar();
renderContent();
setupIntersectionObserver();
});

View File

@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录卡片 - HoloLake Design System</title>
<link rel="stylesheet" href="../components.css">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: var(--bg-primary);
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.container {
width: 100%;
max-width: 400px;
padding: 1rem;
}
.card {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: 16px;
padding: 2rem;
box-shadow: var(--shadow-card);
}
.card h2 {
margin-bottom: 1.5rem;
text-align: center;
color: var(--text-primary);
}
.input-group {
margin-bottom: 1rem;
}
.input-group label {
display: block;
margin-bottom: 0.25rem;
color: var(--text-secondary);
font-size: 0.9rem;
}
.input {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-default);
border-radius: 8px;
background: var(--bg-input);
color: var(--text-primary);
}
.checkbox-row {
display: flex;
align-items: center;
justify-content: space-between;
margin: 1rem 0;
}
.btn-block {
width: 100%;
margin-bottom: 0.5rem;
}
.switch-label {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-secondary);
}
.alert {
margin-top: 1rem;
}
.hidden {
display: none;
}
.register-fields {
margin-top: 1rem;
border-top: 1px solid var(--border-default);
padding-top: 1rem;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h2 id="formTitle">登录</h2>
<div id="loginForm">
<div class="input-group">
<label>用户名</label>
<input type="text" class="input" id="loginUsername" placeholder="请输入用户名">
</div>
<div class="input-group">
<label>密码</label>
<input type="password" class="input" id="loginPassword" placeholder="请输入密码">
</div>
</div>
<div id="registerForm" class="hidden">
<div class="input-group">
<label>用户名</label>
<input type="text" class="input" id="regUsername" placeholder="请输入用户名">
</div>
<div class="input-group">
<label>密码</label>
<input type="password" class="input" id="regPassword" placeholder="请输入密码">
</div>
<div class="input-group">
<label>确认密码</label>
<input type="password" class="input" id="regConfirm" placeholder="请再次输入密码">
</div>
</div>
<div class="checkbox-row">
<label class="switch-label">
<span>记住我</span>
<label class="switch">
<input type="checkbox" id="rememberMe">
<span class="slider"></span>
</label>
</label>
<button class="btn btn-ghost btn-sm" id="toggleFormBtn">注册账号</button>
</div>
<button class="btn btn-primary btn-block" id="actionBtn">登录</button>
<button class="btn btn-outline btn-block" id="switchToRegister" style="display: none;">已有账号?去登录</button>
<div id="messageArea" class="alert hidden"></div>
</div>
</div>
<script src="../components.js"></script>
<script>
(function() {
const loginForm = document.getElementById('loginForm');
const registerForm = document.getElementById('registerForm');
const formTitle = document.getElementById('formTitle');
const actionBtn = document.getElementById('actionBtn');
const toggleFormBtn = document.getElementById('toggleFormBtn');
const switchToRegister = document.getElementById('switchToRegister');
const rememberMe = document.getElementById('rememberMe');
const messageArea = document.getElementById('messageArea');
let isLogin = true;
// 加载记住我状态
const savedRemember = localStorage.getItem('rememberMe');
if (savedRemember === 'true') {
rememberMe.checked = true;
}
// 记住我开关变化时保存
rememberMe.addEventListener('change', function() {
localStorage.setItem('rememberMe', this.checked);
});
function showMessage(type, text) {
messageArea.className = `alert alert-${type}`;
messageArea.textContent = text;
messageArea.classList.remove('hidden');
setTimeout(() => {
messageArea.classList.add('hidden');
}, 3000);
}
function toggleForms() {
isLogin = !isLogin;
if (isLogin) {
loginForm.classList.remove('hidden');
registerForm.classList.add('hidden');
formTitle.textContent = '登录';
actionBtn.textContent = '登录';
toggleFormBtn.textContent = '注册账号';
} else {
loginForm.classList.add('hidden');
registerForm.classList.remove('hidden');
formTitle.textContent = '注册';
actionBtn.textContent = '注册';
toggleFormBtn.textContent = '返回登录';
}
}
toggleFormBtn.addEventListener('click', toggleForms);
actionBtn.addEventListener('click', function() {
if (isLogin) {
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
if (!username || !password) {
showMessage('error', '请输入用户名和密码');
} else {
showMessage('success', '登录成功(模拟)');
}
} else {
const username = document.getElementById('regUsername').value;
const password = document.getElementById('regPassword').value;
const confirm = document.getElementById('regConfirm').value;
if (!username || !password || !confirm) {
showMessage('error', '请填写所有字段');
} else if (password !== confirm) {
showMessage('error', '两次密码不一致');
} else {
showMessage('success', '注册成功(模拟)');
}
}
});
})();
</script>
</body>
</html>

View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>设置面板 - HoloLake Design System</title>
<link rel="stylesheet" href="../components.css">
<style>
body {
background: var(--bg-primary);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 2rem 1rem;
color: var(--text-primary);
}
.settings-container {
max-width: 800px;
margin: 0 auto;
}
.breadcrumb {
margin-bottom: 2rem;
}
.card {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.card h3 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.2rem;
border-bottom: 1px solid var(--border-default);
padding-bottom: 0.5rem;
}
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
}
.setting-label {
color: var(--text-secondary);
}
.progress-container {
margin: 1rem 0;
}
.progress {
margin-bottom: 0.5rem;
}
.avatar-group {
margin: 1rem 0;
}
.button-group {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 1rem;
}
.alert {
margin-top: 1rem;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="settings-container">
<!-- 面包屑 -->
<nav class="breadcrumb">
<a href="#">首页</a> /
<a href="#">设置</a> /
<span>显示设置</span>
</nav>
<!-- 卡片:外观设置 -->
<div class="card">
<h3>外观</h3>
<div class="setting-item">
<span class="setting-label">深色模式</span>
<label class="switch">
<input type="checkbox" id="darkModeToggle">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<span class="setting-label">通知</span>
<label class="switch">
<input type="checkbox" id="notificationToggle">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<span class="setting-label">自动保存</span>
<label class="switch">
<input type="checkbox" id="autoSaveToggle">
<span class="slider"></span>
</label>
</div>
</div>
<!-- 卡片:存储空间 -->
<div class="card">
<h3>存储空间</h3>
<div class="progress-container">
<div class="progress">
<div class="progress-bar" style="width: 65%;" id="storageBar">65%</div>
</div>
<p class="setting-label">已使用 6.5GB / 总空间 10GB</p>
</div>
</div>
<!-- 卡片:团队成员 -->
<div class="card">
<h3>团队成员</h3>
<div class="avatar-group">
<div class="avatar">A</div>
<div class="avatar">B</div>
<div class="avatar">C</div>
<div class="avatar">+2</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="button-group">
<button class="btn btn-outline" id="resetBtn">重置默认</button>
<button class="btn btn-primary" id="saveBtn">保存设置</button>
</div>
<div id="messageArea" class="alert hidden"></div>
</div>
<script src="../components.js"></script>
<script>
(function() {
const darkModeToggle = document.getElementById('darkModeToggle');
const notificationToggle = document.getElementById('notificationToggle');
const autoSaveToggle = document.getElementById('autoSaveToggle');
const saveBtn = document.getElementById('saveBtn');
const resetBtn = document.getElementById('resetBtn');
const messageArea = document.getElementById('messageArea');
const html = document.documentElement;
// 加载本地存储的状态
darkModeToggle.checked = localStorage.getItem('darkMode') === 'true' || false;
notificationToggle.checked = localStorage.getItem('notifications') === 'true' || false;
autoSaveToggle.checked = localStorage.getItem('autoSave') === 'true' || false;
// 深色模式开关联动全局主题
darkModeToggle.addEventListener('change', function() {
html.setAttribute('data-theme', this.checked ? 'dark' : 'light');
localStorage.setItem('darkMode', this.checked);
});
// 初始化主题
if (darkModeToggle.checked) {
html.setAttribute('data-theme', 'dark');
} else {
html.setAttribute('data-theme', 'light');
}
function showMessage(type, text) {
messageArea.className = `alert alert-${type}`;
messageArea.textContent = text;
messageArea.classList.remove('hidden');
setTimeout(() => {
messageArea.classList.add('hidden');
}, 3000);
}
// 保存设置
saveBtn.addEventListener('click', function() {
localStorage.setItem('darkMode', darkModeToggle.checked);
localStorage.setItem('notifications', notificationToggle.checked);
localStorage.setItem('autoSave', autoSaveToggle.checked);
showMessage('success', '设置已保存');
});
// 重置默认
resetBtn.addEventListener('click', function() {
if (confirm('确定要恢复默认设置吗?')) {
darkModeToggle.checked = true; // 默认深色
notificationToggle.checked = false;
autoSaveToggle.checked = false;
// 应用默认
html.setAttribute('data-theme', 'dark');
localStorage.setItem('darkMode', 'true');
localStorage.setItem('notifications', 'false');
localStorage.setItem('autoSave', 'false');
showMessage('info', '已恢复默认设置');
}
});
})();
</script>
</body>
</html>