Skills 将特定领域的知识和工作流打包封装,供 Agent 调用以完成特定任务。 (全世界免费可以用的一个模板库&&这个模板库支持普通语言就可以命中对应风格,加上你的需求描述动态生成&&可用性高) 参考:cursor skills
兼容Claude:
js.claude/
skills/
frontend-design/
LICENSE.txt
cursor本身的:
js.cursor/skills/
参考引入:
bashhttps://github.com/anthropics/skills/tree/main/skills 我希望你可以将这个github库下面的所有skills导入进去
之后就可以chat中通过/或@来直接引用
bash/pdf\SKILL.md 将index.html生成容易理解,简洁的PDF
以下参考:https://juejin.cn/post/7593362940071903284
在上面基础上,加入了个人实践对比及若干改动点,只为了呈现出对比区别:
生成塔罗牌:
js我要做一个 H5 ,功能是一个塔罗牌。不要用任何skills,就直接默认使用大模型本身的能力!
你是一名经验丰富的产品设计专家和资深前端专家,擅长UI构图与前端页面还原。现在请你帮我完成这个塔罗牌应用的 UI/UX 原型图设计。请输出一个包含所有设计页面的完整HTML文件,用于展示完整UI界面。
注意:生成代码的时候请一步一步执行,避免单步任务过大,时间执行过长
直接在左侧目录生成可运行的代码,不要动原来的代码。
js使用 Skill front-design。我要做一个 H5 ,功能是一个塔罗牌。
你是一名经验丰富的产品设计专家和资深前端专家,擅长UI构图与前端页面还原。现在请你帮我完成这个塔罗牌应用的 UI/UX 原型图设计。请输出一个包含所有设计页面的完整HTML文件,用于展示完整UI界面。
注意:生成代码的时候请一步一步执行,避免单步任务过大,时间执行过长
(上面的会自动命中skills) 或
js/frontend-design\SKILL.md 使用 Skill front-design。我要做一个 H5 ,功能是一个塔罗牌。
你是一名经验丰富的产品设计专家和资深前端专家,擅长UI构图与前端页面还原。现在请你帮我完成这个塔罗牌应用的 UI/UX 原型图设计。请输出一个包含所有设计页面的完整HTML文件,用于展示完整UI界面。
注意:生成代码的时候请一步一步执行,避免单步任务过大,时间执行过长
直接在左侧目录生成可运行的代码
使用了skills,生成的代码不需要做任何修改,可用度和风格完整性很强,不管是展示界面、还是点击使用、还是过程特性,都不需要做任何修改,直接演示即可。
不使用skills,过程发现了几个问题:
html<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Moon Arcana · 塔罗占卜</title>
<meta name="description" content="Moon Arcana 塔罗 H5,占卜、抽牌、解读,一页完成。">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700&family=Noto+Serif+SC:wght@300;500;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #050612;
--bg-elevated: rgba(10, 12, 26, 0.96);
--bg-soft: rgba(16, 18, 40, 0.92);
--accent: #eccb7c;
--accent-soft: rgba(236, 203, 124, 0.22);
--accent-strong: #f7da92;
--accent-danger: #ff6b81;
--text-main: #f7f0e8;
--text-muted: #a6a4c1;
--border-subtle: rgba(255, 255, 255, 0.08);
--card-back: radial-gradient(circle at 20% 10%, #3f2849 0, #0f172a 40%, #050612 100%);
--card-front: rgba(14, 16, 36, 0.98);
--radius-xl: 24px;
--radius-lg: 18px;
--radius-md: 12px;
--shadow-soft: 0 22px 60px rgba(5, 6, 18, 0.95);
--shadow-glow: 0 0 60px rgba(236, 203, 124, 0.4);
--noise: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='noStitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.22'/></svg>");
}
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
font-family: 'Noto Serif SC', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: var(--text-main);
background: radial-gradient(circle at top, #1a2442 0, #050612 52%, #02020a 100%);
overscroll-behavior-y: contain;
scroll-behavior: smooth;
}
body::before {
content: "";
position: fixed;
inset: 0;
background-image:
radial-gradient(circle at 10% -10%, rgba(236, 203, 124, 0.13), transparent 55%),
radial-gradient(circle at 90% 110%, rgba(85, 194, 214, 0.13), transparent 55%);
mix-blend-mode: screen;
opacity: 0.9;
pointer-events: none;
z-index: -2;
}
body::after {
content: "";
position: fixed;
inset: 0;
background-image: var(--noise);
mix-blend-mode: soft-light;
opacity: 0.52;
pointer-events: none;
z-index: -1;
}
.app-shell {
min-height: 100vh;
max-width: 480px;
margin: 0 auto;
padding: env(safe-area-inset-top, 20px) 16px env(safe-area-inset-bottom, 22px);
display: flex;
flex-direction: column;
gap: 16px;
position: relative;
}
.chrome {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 2px 2px;
}
.brand {
display: flex;
align-items: center;
gap: 8px;
}
.brand-mark {
width: 26px;
height: 26px;
border-radius: 999px;
border: 1px solid rgba(236, 203, 124, 0.7);
background:
radial-gradient(circle at 30% 0%, rgba(250, 235, 195, 0.8), transparent 55%),
radial-gradient(circle at 70% 120%, rgba(84, 164, 210, 0.7), transparent 60%),
#050612;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.07), 0 0 26px rgba(236, 203, 124, 0.8);
position: relative;
overflow: hidden;
}
.brand-mark::before {
content: "";
position: absolute;
inset: 4px;
border-radius: inherit;
border: 1px dashed rgba(255, 255, 255, 0.35);
border-top: none;
opacity: 0.9;
}
.brand-text {
display: flex;
flex-direction: column;
gap: 1px;
}
.brand-name {
font-family: 'Cinzel', serif;
font-size: 16px;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.brand-sub {
font-size: 11px;
color: var(--text-muted);
letter-spacing: 0.28em;
text-transform: uppercase;
}
.chrome-actions {
display: flex;
align-items: center;
gap: 10px;
}
.icon-button {
width: 32px;
height: 32px;
border-radius: 999px;
border: 1px solid var(--border-subtle);
background: radial-gradient(circle at 30% 0%, rgba(255, 255, 255, 0.12), transparent 60%), rgba(10, 13, 28, 0.9);
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
font-size: 16px;
cursor: pointer;
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.55);
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.icon-button span {
transform: translateY(0.5px);
}
.icon-button.active,
.icon-button:active {
transform: translateY(1px) scale(0.97);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.65);
border-color: rgba(236, 203, 124, 0.9);
color: var(--accent-strong);
background: radial-gradient(circle at 30% 0%, rgba(236, 203, 124, 0.3), transparent 65%), rgba(15, 18, 38, 0.96);
}
main {
flex: 1;
display: flex;
flex-direction: column;
gap: 14px;
}
.hero-card {
border-radius: var(--radius-xl);
padding: 18px 18px 14px;
background:
radial-gradient(circle at -20% 0%, rgba(250, 230, 172, 0.16), transparent 58%),
radial-gradient(circle at 110% 40%, rgba(84, 164, 210, 0.26), transparent 55%),
linear-gradient(135deg, rgba(9, 10, 30, 0.96), rgba(9, 10, 26, 0.98));
box-shadow: var(--shadow-soft);
border: 1px solid rgba(255, 255, 255, 0.06);
position: relative;
overflow: hidden;
isolation: isolate;
}
.hero-card::before {
content: "";
position: absolute;
inset: -30%;
background-image: repeating-radial-gradient(circle at 10% 0%, rgba(255, 255, 255, 0.05) 0, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 8px);
opacity: 0.6;
mix-blend-mode: soft-light;
pointer-events: none;
z-index: -1;
}
.hero-top {
display: flex;
justify-content: space-between;
gap: 14px;
}
.hero-copy {
max-width: 62%;
}
.hero-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 2px 9px;
border-radius: 99px;
border: 1px solid rgba(236, 203, 124, 0.8);
background: linear-gradient(135deg, rgba(10, 11, 28, 0.95), rgba(30, 25, 60, 0.9));
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent-strong);
margin-bottom: 6px;
}
.hero-tag-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: radial-gradient(circle, #ffe9b8 0, #eccb7c 40%, rgba(236, 203, 124, 0.2) 100%);
box-shadow: 0 0 18px rgba(236, 203, 124, 0.9);
}
.hero-title-main {
font-family: 'Cinzel', serif;
font-size: 26px;
letter-spacing: 0.18em;
text-transform: uppercase;
margin-bottom: 6px;
}
.hero-title-sub {
font-size: 14px;
color: var(--text-muted);
}
.hero-moon-orbit {
position: relative;
width: 105px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.orbit-ring {
width: 90px;
height: 90px;
border-radius: 999px;
border: 1px dashed rgba(236, 203, 124, 0.55);
border-left-color: transparent;
border-bottom-color: transparent;
transform: rotate(-25deg);
position: relative;
box-shadow: 0 0 40px rgba(236, 203, 124, 0.45);
}
.orbit-moon {
position: absolute;
inset: 15px;
border-radius: inherit;
background: radial-gradient(circle at 18% 0, #fff7dc 0, #f3d291 40%, #f0b875 60%, #f3f0e6 100%);
box-shadow: 0 0 36px rgba(236, 203, 124, 0.9);
overflow: hidden;
}
.orbit-moon::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(circle at 65% 35%, rgba(5, 6, 18, 0.75), transparent 55%);
mix-blend-mode: multiply;
}
.orbit-dot {
position: absolute;
width: 6px;
height: 6px;
border-radius: 999px;
background: #f7da92;
box-shadow: 0 0 12px rgba(247, 218, 146, 0.9);
top: 1px;
left: 50%;
transform: translateX(-50%);
}
.hero-meta {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: 14px;
gap: 8px;
font-size: 11px;
}
.hero-meta-col {
display: flex;
flex-direction: column;
gap: 4px;
}
.hero-meta-label {
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--text-muted);
}
.hero-meta-value {
display: flex;
align-items: baseline;
gap: 4px;
}
.hero-meta-value span {
color: var(--accent-strong);
}
.hero-cta {
display: flex;
gap: 8px;
align-items: center;
}
.primary-btn {
flex: 1;
border: none;
border-radius: 999px;
padding: 10px 16px;
font-family: inherit;
font-size: 15px;
letter-spacing: 0.16em;
text-transform: uppercase;
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.36), transparent 62%),
linear-gradient(135deg, #f7da92, #e8b96f);
color: #20140c;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.6);
position: relative;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease, opacity 0.15s ease;
}
.primary-btn:active {
transform: translateY(1px) scale(0.98);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.8);
filter: brightness(0.95);
opacity: 0.9;
}
.primary-btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, rgba(255, 255, 255, 0.4), transparent 40%);
mix-blend-mode: soft-light;
opacity: 0.7;
pointer-events: none;
}
.primary-btn-icon {
width: 18px;
height: 18px;
border-radius: 999px;
border: 1px solid rgba(32, 20, 12, 0.4);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
background: rgba(255, 255, 255, 0.55);
}
.secondary-link {
font-size: 12px;
color: var(--text-muted);
text-decoration: underline;
text-underline-offset: 4px;
opacity: 0.9;
}
.section {
border-radius: var(--radius-xl);
padding: 12px 14px 12px;
background: var(--bg-soft);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.85);
border: 1px solid var(--border-subtle);
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.section-title {
font-size: 13px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text-muted);
}
.section-badge {
padding: 2px 8px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.18);
font-size: 10px;
color: var(--accent-strong);
letter-spacing: 0.16em;
text-transform: uppercase;
}
.layout-tabs {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.layout-pill {
flex: 1;
padding: 7px 10px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.04);
background: radial-gradient(circle at 10% 0%, rgba(255, 255, 255, 0.08), transparent 55%), rgba(10, 11, 28, 0.9);
font-size: 11px;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--text-muted);
cursor: pointer;
position: relative;
overflow: hidden;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.15s ease;
}
.layout-pill span:first-child {
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 10px;
}
.layout-pill span:last-child {
font-size: 11px;
opacity: 0.8;
}
.layout-pill.active {
border-color: rgba(236, 203, 124, 0.7);
color: var(--accent-strong);
background: radial-gradient(circle at 20% 0%, rgba(247, 218, 146, 0.45), transparent 65%), rgba(17, 16, 33, 0.96);
box-shadow: var(--shadow-glow);
transform: translateY(-1px);
}
.layout-pill::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, rgba(255, 255, 255, 0.2), transparent 50%);
mix-blend-mode: soft-light;
opacity: 0;
transition: opacity 0.2s ease;
}
.layout-pill.active::after {
opacity: 0.9;
}
.spread-stage {
border-radius: var(--radius-lg);
padding: 10px 10px 10px;
background: radial-gradient(circle at 0 0, rgba(112, 146, 210, 0.18), transparent 60%), linear-gradient(145deg, rgba(5, 7, 21, 0.98), rgba(4, 6, 18, 0.98));
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.85);
position: relative;
overflow: hidden;
}
.spread-stage::before {
content: "";
position: absolute;
inset: -20%;
background-image: radial-gradient(circle at 30% 0%, rgba(236, 203, 124, 0.18), transparent 60%);
opacity: 0;
transition: opacity 0.5s ease;
pointer-events: none;
}
.spread-stage.is-shuffling::before {
opacity: 1;
animation: halo-pulse 1.4s ease-in-out infinite;
}
@keyframes halo-pulse {
0%, 100% { opacity: 0.2; transform: translateY(0); }
50% { opacity: 0.8; transform: translateY(-4px); }
}
.spread-guides {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
font-size: 10px;
color: var(--text-muted);
letter-spacing: 0.18em;
text-transform: uppercase;
}
.spread-guides .dotline {
flex: 1;
height: 1px;
border-top: 1px dashed rgba(255, 255, 255, 0.18);
margin: 0 6px;
}
.deck {
margin-top: 4px;
padding: 4px 0 8px;
position: relative;
height: 150px;
}
.deck-stack {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 104px;
height: 154px;
perspective: 1200px;
}
.tarot-card {
width: 100%;
height: 100%;
border-radius: 18px;
position: absolute;
left: 0;
top: 0;
background: var(--card-back);
border: 1px solid rgba(248, 234, 192, 0.9);
box-shadow: 0 18px 40px rgba(3, 4, 12, 0.96);
cursor: pointer;
transform-origin: center center;
transform-style: preserve-3d;
backface-visibility: hidden;
transition: transform 400ms cubic-bezier(0.19, 1, 0.22, 1), box-shadow 0.4s ease, filter 0.3s ease, opacity 0.25s ease;
}
.tarot-card-back-inner {
position: absolute;
inset: 7px;
border-radius: 13px;
border: 1px solid rgba(236, 203, 124, 0.9);
background:
radial-gradient(circle at 50% 0, rgba(255, 252, 240, 0.7), transparent 55%),
repeating-linear-gradient(135deg, rgba(237, 232, 216, 0.32) 0, rgba(237, 232, 216, 0.32) 1px, transparent 1px, transparent 6px),
rgba(6, 8, 20, 0.96);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.tarot-sigil {
width: 78px;
height: 78px;
border-radius: 999px;
border: 1px solid rgba(17, 23, 50, 0.3);
background:
radial-gradient(circle at 50% 0, rgba(247, 218, 146, 0.85), transparent 60%),
conic-gradient(from 220deg, rgba(12, 17, 35, 0.96), rgba(16, 23, 51, 0.96), rgba(12, 17, 35, 0.96));
position: relative;
box-shadow: 0 0 35px rgba(236, 203, 124, 0.7);
}
.tarot-sigil-ring {
position: absolute;
inset: 9px;
border-radius: inherit;
border: 1px dashed rgba(13, 16, 36, 0.9);
border-bottom: none;
}
.tarot-sigil-star {
position: absolute;
inset: 18px;
border-radius: inherit;
border: 1px solid rgba(243, 237, 219, 0.85);
clip-path: polygon(50% 2%, 65% 35%, 98% 35%, 72% 57%, 82% 94%, 50% 72%, 18% 94%, 28% 57%, 2% 35%, 35% 35%);
box-shadow: inset 0 0 18px rgba(255, 255, 255, 0.9);
}
.tarot-sigil-glyph {
position: absolute;
inset: 28px;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Cinzel', serif;
font-size: 14px;
letter-spacing: 0.4em;
text-transform: uppercase;
}
.tarot-card:nth-child(1) { transform: translate3d(0, 0, 0) rotate(0deg); }
.tarot-card:nth-child(2) { transform: translate3d(1px, 2px, -10px) rotate(-1deg); opacity: 0.97; }
.tarot-card:nth-child(3) { transform: translate3d(-1px, 4px, -20px) rotate(1deg); opacity: 0.94; }
.tarot-card.is-hovered {
transform: translate3d(0, -10px, 30px) rotate(0deg);
box-shadow: 0 28px 80px rgba(0, 0, 0, 0.98);
filter: saturate(1.12);
}
.tarot-card.is-drawn {
transform: translate3d(0, -34px, 120px) scale(1.02) rotate(0deg);
box-shadow: 0 40px 90px rgba(0, 0, 0, 0.98), 0 0 60px rgba(236, 203, 124, 0.8);
}
.deck-subtext {
text-align: center;
font-size: 11px;
color: var(--text-muted);
margin-top: 6px;
}
.deck-subtext-strong {
color: var(--accent-strong);
letter-spacing: 0.18em;
text-transform: uppercase;
font-size: 10px;
}
.insight {
display: grid;
grid-template-columns: 1.4fr 2fr;
gap: 8px;
margin-top: 10px;
font-size: 12px;
}
.insight-label {
text-transform: uppercase;
letter-spacing: 0.16em;
color: var(--text-muted);
font-size: 10px;
}
.insight-main {
color: var(--text-main);
}
.insight-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 6px;
}
.chip {
border-radius: 999px;
padding: 3px 7px;
font-size: 10px;
border: 1px solid rgba(255, 255, 255, 0.16);
background: rgba(7, 9, 26, 0.9);
color: var(--text-muted);
display: inline-flex;
align-items: center;
gap: 4px;
}
.chip-dot {
width: 5px;
height: 5px;
border-radius: 999px;
background: var(--accent-strong);
}
.bottom-bar {
margin-top: auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
font-size: 11px;
color: var(--text-muted);
}
.bottom-bar-meta {
display: flex;
flex-direction: column;
gap: 2px;
}
.bottom-bar-meter {
width: 100px;
height: 4px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.04);
overflow: hidden;
position: relative;
}
.bottom-bar-meter-fill {
position: absolute;
inset: 0;
width: 45%;
border-radius: inherit;
background: linear-gradient(90deg, rgba(236, 203, 124, 0.8), rgba(112, 178, 215, 0.9));
box-shadow: 0 0 16px rgba(236, 203, 124, 0.7);
}
.bottom-bar-actions {
display: flex;
gap: 8px;
}
.ghost-btn {
padding: 6px 10px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.14);
background: rgba(5, 7, 22, 0.9);
font-size: 11px;
color: var(--text-muted);
display: inline-flex;
align-items: center;
gap: 4px;
cursor: pointer;
letter-spacing: 0.14em;
text-transform: uppercase;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.15s ease;
}
.ghost-btn span {
font-size: 13px;
}
.ghost-btn:active {
background: rgba(16, 18, 38, 0.96);
border-color: rgba(236, 203, 124, 0.7);
color: var(--accent-strong);
transform: translateY(1px);
}
.sheet {
position: fixed;
left: 50%;
bottom: 0;
transform: translateX(-50%) translateY(100%);
width: 100%;
max-width: 520px;
max-height: min(76vh, 620px);
background: linear-gradient(to top, rgba(3, 4, 10, 0.98), rgba(12, 16, 32, 0.98));
border-radius: 24px 24px 0 0;
box-shadow: 0 -22px 60px rgba(0, 0, 0, 0.96);
border-top: 1px solid rgba(236, 203, 124, 0.4);
border-inline: 1px solid rgba(255, 255, 255, 0.06);
padding: 10px 18px 18px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 40;
opacity: 0;
pointer-events: none;
transition: transform 260ms cubic-bezier(0.19, 1, 0.22, 1), opacity 200ms ease;
}
.sheet.visible {
transform: translateX(-50%) translateY(0%);
opacity: 1;
pointer-events: auto;
}
.sheet-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.sheet-title {
display: flex;
flex-direction: column;
gap: 2px;
}
.sheet-eyebrow {
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text-muted);
}
.sheet-heading {
font-size: 16px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.sheet-close {
width: 30px;
height: 30px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.18);
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
background: rgba(8, 10, 26, 0.96);
cursor: pointer;
font-size: 15px;
}
.sheet-handle {
align-self: center;
width: 44px;
height: 4px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.18);
margin-top: -2px;
}
.sheet-body {
flex: 1;
overflow-y: auto;
padding: 4px 0 0;
font-size: 13px;
color: var(--text-main);
}
.sheet-body::-webkit-scrollbar {
width: 4px;
}
.sheet-body::-webkit-scrollbar-track {
background: transparent;
}
.sheet-body::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.18);
border-radius: 999px;
}
.result-layout {
display: flex;
gap: 10px;
margin-bottom: 12px;
}
.result-card {
width: 110px;
min-width: 110px;
height: 170px;
border-radius: 18px;
background: var(--card-front);
border: 1px solid rgba(247, 218, 146, 0.7);
box-shadow: 0 18px 46px rgba(0, 0, 0, 0.9);
position: relative;
overflow: hidden;
}
.result-card-header {
position: absolute;
top: 8px;
left: 0;
right: 0;
text-align: center;
font-size: 10px;
color: var(--text-muted);
letter-spacing: 0.18em;
text-transform: uppercase;
}
.result-card-art {
position: absolute;
inset: 26px 12px 38px;
border-radius: 14px;
border: 1px solid rgba(219, 192, 142, 0.7);
overflow: hidden;
background:
radial-gradient(circle at 50% 0, rgba(251, 240, 211, 0.95), transparent 55%),
conic-gradient(from 200deg, #f2c68c, #f7da92, #e8b96f, #f2c68c);
display: flex;
align-items: center;
justify-content: center;
}
.result-card-symbol {
width: 44px;
height: 44px;
border-radius: 999px;
border: 1px solid rgba(122, 92, 58, 0.9);
background:
radial-gradient(circle at 50% 0, rgba(255, 255, 255, 0.8), transparent 60%),
linear-gradient(140deg, #f0c493, #d69650);
display: flex;
align-items: center;
justify-content: center;
font-family: 'Cinzel', serif;
font-size: 20px;
color: #2c1609;
}
.result-card-footer {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
text-align: center;
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.result-meta {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.result-chip-row {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.result-name {
font-size: 16px;
letter-spacing: 0.06em;
}
.result-position {
font-size: 11px;
color: var(--accent-strong);
letter-spacing: 0.18em;
text-transform: uppercase;
}
.result-paragraph {
font-size: 13px;
line-height: 1.6;
color: var(--text-main);
opacity: 0.96;
}
.result-section-title {
margin-top: 10px;
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text-muted);
}
.result-bullets {
margin-top: 6px;
padding-left: 16px;
font-size: 12px;
line-height: 1.7;
color: var(--text-main);
}
.history-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
font-size: 12px;
}
.history-item {
padding: 8px 10px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(8, 10, 26, 0.96);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.history-main {
display: flex;
flex-direction: column;
gap: 3px;
}
.history-meta {
font-size: 11px;
color: var(--text-muted);
}
.history-pill {
padding: 2px 8px;
border-radius: 999px;
border: 1px solid rgba(236, 203, 124, 0.55);
font-size: 10px;
color: var(--accent-strong);
letter-spacing: 0.18em;
text-transform: uppercase;
}
.guide-section {
margin-bottom: 10px;
}
.guide-title {
font-size: 13px;
margin-bottom: 4px;
}
.guide-text {
font-size: 12px;
line-height: 1.7;
color: var(--text-main);
opacity: 0.96;
}
.guide-steps {
margin-top: 6px;
padding-left: 16px;
font-size: 12px;
line-height: 1.7;
color: var(--text-main);
}
.overlay-mask {
position: fixed;
inset: 0;
background: radial-gradient(circle at top, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.96));
backdrop-filter: blur(18px);
opacity: 0;
pointer-events: none;
transition: opacity 200ms ease;
z-index: 30;
}
.overlay-mask.visible {
opacity: 1;
pointer-events: auto;
}
@media (max-width: 360px) {
.hero-title-main {
font-size: 22px;
}
.hero-card {
padding: 14px 12px 10px;
}
.hero-moon-orbit {
display: none;
}
.result-layout {
flex-direction: column;
}
.result-card {
margin: 0 auto;
}
.sheet {
border-radius: 18px 18px 0 0;
}
}
@media (min-width: 520px) {
.app-shell {
padding-top: 28px;
padding-bottom: 26px;
}
}
</style>
</head>
<body>
<div class="app-shell">
<header class="chrome">
<div class="brand">
<div class="brand-mark"></div>
<div class="brand-text">
<div class="brand-name">Moon Arcana</div>
<div class="brand-sub">Tarot Studio</div>
</div>
</div>
<div class="chrome-actions">
<button class="icon-button" id="btnHistory" aria-label="历史记录">
<span>☽</span>
</button>
<button class="icon-button" id="btnGuide" aria-label="使用指引">
<span>?</span>
</button>
</div>
</header>
<main>
<section class="hero-card">
<div class="hero-top">
<div class="hero-copy">
<div class="hero-tag">
<span class="hero-tag-dot"></span>
<span>即时占卜 · 单张牌</span>
</div>
<div class="hero-title-main">TAROT</div>
<div class="hero-title-sub">
闭上眼,默念心中问题,<br>
在月光下,为自己抽一张牌。
</div>
</div>
<div class="hero-moon-orbit">
<div class="orbit-ring">
<div class="orbit-moon"></div>
<div class="orbit-dot"></div>
</div>
</div>
</div>
<div class="hero-meta">
<div class="hero-meta-col">
<div class="hero-meta-label">今日月相</div>
<div class="hero-meta-value">
<span id="moonPhase">···</span>
</div>
</div>
<div class="hero-meta-col hero-cta">
<button class="primary-btn" id="btnDraw">
<span>开始抽牌</span>
<span class="primary-btn-icon">➝</span>
</button>
</div>
</div>
</section>
<section class="section">
<div class="section-header">
<div class="section-title">抽牌布局</div>
<div class="section-badge">单牌心占 · H5</div>
</div>
<div class="layout-tabs">
<button class="layout-pill active" data-layout="single">
<span>Single</span>
<span>单张牌 · 当下心境</span>
</button>
<button class="layout-pill" data-layout="three">
<span>3-Path</span>
<span>过去 / 现在 / 未来</span>
</button>
</div>
<div class="spread-stage" id="spreadStage">
<div class="spread-guides">
<span>洗牌中</span>
<div class="dotline"></div>
<span>当你准备好,点按抽牌</span>
</div>
<div class="deck">
<div class="deck-stack" id="deck">
<div class="tarot-card">
<div class="tarot-card-back-inner">
<div class="tarot-sigil">
<div class="tarot-sigil-ring"></div>
<div class="tarot-sigil-star"></div>
<div class="tarot-sigil-glyph">MA</div>
</div>
</div>
</div>
<div class="tarot-card">
<div class="tarot-card-back-inner">
<div class="tarot-sigil">
<div class="tarot-sigil-ring"></div>
<div class="tarot-sigil-star"></div>
<div class="tarot-sigil-glyph">MA</div>
</div>
</div>
</div>
<div class="tarot-card">
<div class="tarot-card-back-inner">
<div class="tarot-sigil">
<div class="tarot-sigil-ring"></div>
<div class="tarot-sigil-star"></div>
<div class="tarot-sigil-glyph">MA</div>
</div>
</div>
</div>
</div>
</div>
<div class="deck-subtext">
<div class="deck-subtext-strong">触摸并抽取一张牌</div>
<div id="deckMessage">请将注意力停留在一个具体的问题上。</div>
</div>
</div>
<div class="insight">
<div>
<div class="insight-label">此刻的你</div>
<div class="insight-main" id="insightMood">情绪起伏略大,适合用一张牌安静地照亮内心。</div>
</div>
<div>
<div class="insight-label">建议使用场景</div>
<div class="insight-main">情绪整理 / 关系困惑 / 重要决策前的小确认。</div>
</div>
</div>
<div class="insight-tags">
<div class="chip">
<span class="chip-dot"></span>
<span>约 1 分钟完成</span>
</div>
<div class="chip">
<span class="chip-dot"></span>
<span>无需账号 · 本地保存</span>
</div>
<div class="chip">
<span class="chip-dot"></span>
<span>支持竖屏 / 手机浏览器</span>
</div>
</div>
</section>
</main>
<footer class="bottom-bar">
<div class="bottom-bar-meta">
<div>Moon Arcana · 为自己留一刻安静</div>
<div class="bottom-bar-meter">
<div class="bottom-bar-meter-fill" id="meterFill"></div>
</div>
</div>
<div class="bottom-bar-actions">
<button class="ghost-btn" id="btnReset">
<span>↺</span>
<span>重置</span>
</button>
</div>
</footer>
</div>
<div class="overlay-mask" id="overlay"></div>
<section class="sheet" id="sheetResult" aria-hidden="true">
<div class="sheet-handle"></div>
<div class="sheet-header">
<div class="sheet-title">
<div class="sheet-eyebrow">抽牌结果</div>
<div class="sheet-heading" id="resultHeading">——</div>
</div>
<button class="sheet-close" data-close-sheet="result" aria-label="关闭结果">
✕
</button>
</div>
<div class="sheet-body">
<div class="result-layout">
<div class="result-card">
<div class="result-card-header">Moon Arcana</div>
<div class="result-card-art">
<div class="result-card-symbol" id="resultSymbol">0</div>
</div>
<div class="result-card-footer" id="resultShort">——</div>
</div>
<div class="result-meta">
<div class="result-chip-row">
<div class="chip">
<span class="chip-dot"></span>
<span id="resultType">大阿尔卡纳</span>
</div>
<div class="chip">
<span class="chip-dot"></span>
<span id="resultOrientation">正位</span>
</div>
</div>
<div class="result-name" id="resultName">——</div>
<div class="result-position" id="resultPositionLabel">此牌正在回应「当下心境」</div>
<p class="result-paragraph" id="resultIntro">
—— 占卜尚未开始,请先在月光下抽一张牌。
</p>
</div>
</div>
<div class="result-section-title">牌义摘要</div>
<p class="result-paragraph" id="resultSummary">
当你准备好提问时,这里会为你呈现一段简短但直白的说明,帮助你抓住问题的核心。
</p>
<div class="result-section-title">给你的三条提示</div>
<ul class="result-bullets" id="resultTips">
<li>有时候答案不是「要不要」,而是「先照顾好自己」。</li>
<li>你可以把看到的第一句话,记在心里或写在纸上。</li>
<li>这不是命令,而是一面镜子 —— 你依然拥有选择。</li>
</ul>
</div>
</section>
<section class="sheet" id="sheetHistory" aria-hidden="true">
<div class="sheet-handle"></div>
<div class="sheet-header">
<div class="sheet-title">
<div class="sheet-eyebrow">占卜记录</div>
<div class="sheet-heading">本机最近 5 次</div>
</div>
<button class="sheet-close" data-close-sheet="history" aria-label="关闭历史">
✕
</button>
</div>
<div class="sheet-body">
<div class="history-list" id="historyList">
<div class="history-item">
<div class="history-main">
<div>暂无记录</div>
<div class="history-meta">首次使用时,我们会在这里为你保留最近的牌面。</div>
</div>
<div class="history-pill">Empty</div>
</div>
</div>
</div>
</section>
<section class="sheet" id="sheetGuide" aria-hidden="true">
<div class="sheet-handle"></div>
<div class="sheet-header">
<div class="sheet-title">
<div class="sheet-eyebrow">使用指南</div>
<div class="sheet-heading">如何与这副牌相处</div>
</div>
<button class="sheet-close" data-close-sheet="guide" aria-label="关闭指南">
✕
</button>
</div>
<div class="sheet-body">
<div class="guide-section">
<div class="guide-title">1. 先问自己,而不是问牌</div>
<p class="guide-text">
在点击「开始抽牌」前,先默念一个清晰的问题。<br>
与其问「TA 爱不爱我」,不如问:「在这段关系中,我真正需要看见的是什么?」。
</p>
</div>
<div class="guide-section">
<div class="guide-title">2. 不要疯狂刷新</div>
<p class="guide-text">
你当然可以反复抽牌,但 Moon Arcana 更鼓励你:<br>
一次抽取,一次认真阅读,一次真正停下来面对自己。
</p>
<ul class="guide-steps">
<li>抽到的第一张牌,往往最接近你当下的直觉。</li>
<li>如果真的想再次占卜,请先换一个提问角度。</li>
<li>历史记录只存储在本地浏览器,不会同步到任何服务端。</li>
</ul>
</div>
<div class="guide-section">
<div class="guide-title">3. 把解读当作「协商」,不是「宣判」</div>
<p class="guide-text">
牌义是一个邀请,而不是命令。你完全可以不同意某些句子,也可以只带走最打动你的那一句。<br>
如果某段话让你不舒服,请温柔地对自己说:「谢谢你提醒我,我会用适合自己的方式走下去」。
</p>
</div>
<div class="guide-section">
<div class="guide-title">4. 关于隐私与边界</div>
<p class="guide-text">
占卜记录仅保存在你的设备浏览器中,不会上传到云端。<br>
如果你在公共环境下使用,建议抽完牌后长按电源键或锁屏,让这段对话只属于你自己。
</p>
</div>
</div>
</section>
<script>
const deckEl = document.getElementById('deck');
const cards = Array.from(deckEl.querySelectorAll('.tarot-card'));
const spreadStage = document.getElementById('spreadStage');
const btnDraw = document.getElementById('btnDraw');
const deckMessage = document.getElementById('deckMessage');
const overlay = document.getElementById('overlay');
const sheetResult = document.getElementById('sheetResult');
const sheetHistory = document.getElementById('sheetHistory');
const sheetGuide = document.getElementById('sheetGuide');
const btnHistory = document.getElementById('btnHistory');
const btnGuide = document.getElementById('btnGuide');
const btnReset = document.getElementById('btnReset');
const layoutPills = Array.from(document.querySelectorAll('.layout-pill'));
const meterFill = document.getElementById('meterFill');
const moonPhaseEl = document.getElementById('moonPhase');
const resultHeading = document.getElementById('resultHeading');
const resultSymbol = document.getElementById('resultSymbol');
const resultShort = document.getElementById('resultShort');
const resultType = document.getElementById('resultType');
const resultOrientation = document.getElementById('resultOrientation');
const resultName = document.getElementById('resultName');
const resultPositionLabel = document.getElementById('resultPositionLabel');
const resultIntro = document.getElementById('resultIntro');
const resultSummary = document.getElementById('resultSummary');
const resultTips = document.getElementById('resultTips');
const historyList = document.getElementById('historyList');
let isShuffling = false;
let hasDrawn = false;
let currentLayout = 'single';
let currentResult = null;
const TAROT_POOL = [
{
id: 0,
name: '愚者',
short: '新的旅程',
type: '大阿尔卡纳',
symbol: '0',
summary: '愚者代表一切的起点 —— 你正站在悬崖边,手里只拿着最重要的行囊,准备迈向一个未知却诚实的方向。',
intro: '这张牌并不是鼓励你盲目冒险,而是提醒你:你拥有重新出发的资格。也许你并没有准备好所有答案,但你已经厌倦停留在原地。',
tips: [
'允许自己在「不知道」中迈出小小一步,而不是逼迫自己马上看见全程路线。',
'你可以把这段经历当成一次试验,而不是一次考试 —— 失败并不会让你失去被爱的资格。',
'注意区分「真心的好奇」与「为了逃避而冲动」。前者会让你更靠近自己。'
]
},
{
id: 1,
name: '魔术师',
short: '自我主导',
type: '大阿尔卡纳',
symbol: 'Ⅰ',
summary: '魔术师像是一条介于天地之间的闪电,提醒你:答案并不在外面,而在你愿不愿意使用手上已有的工具。',
intro: '也许你正在寻找一个「完美时机」,但魔术师告诉你:当你开始行动,那一刻就是对的时机。',
tips: [
'写下你已经拥有的三样资源(能力、人脉、时间、经验),并尝试用它们拼出一个最小行动方案。',
'避免一次性承诺太多,先完成一个微小而可验证的动作。',
'如果你习惯把自己放低,请允许自己在这个议题上稍微「自信一点点」。'
]
},
{
id: 2,
name: '女祭司',
short: '直觉之海',
type: '大阿尔卡纳',
symbol: 'Ⅱ',
summary: '女祭司安静地坐在门槛上,身后是帷幕与深海。她不急着给出答案,她在等你安静下来,听见自己真正的声音。',
intro: '你现在最需要的不是更多信息,而是给自己一段没有干扰的时间,好好区分:哪些是别人的期待,哪些才是你心里真正在意的事情。',
tips: [
'本周找一个不被打扰的 20 分钟,只和自己待在一起,把心里最吵的三句话写下来。',
'当你感到犹豫时,先不要做决定,把注意力放回身体:是哪里在紧绷?哪里在松开?',
'如果一件事让你感到「隐隐不对劲」,那已经是一种答案。你可以选择慢一点。'
]
},
{
id: 3,
name: '恋人',
short: '真诚选择',
type: '大阿尔卡纳',
symbol: 'Ⅵ',
summary: '恋人牌并不仅仅指爱情,它更关乎「我是否愿意为这个选择承担真诚的承诺」。',
intro: '你很有可能正站在一条分岔路口。无论是关系、工作还是生活方式,这张牌提醒你 —— 你在选择的不只是对象,而是想成为的那种自己。',
tips: [
'把你正在纠结的两个选项分别写下,并补上一句:「当我选择它时,我是在成为什么样的人?」',
'注意那些让你必须不断「说服自己」的决定,它们也许并不那么适合你。',
'允许关系里存在不完美的部分,但不要牺牲掉最基本的尊重与安宁感。'
]
},
{
id: 4,
name: '力量',
short: '柔软的勇气',
type: '大阿尔卡纳',
symbol: 'Ⅷ',
summary: '力量不是咬紧牙关硬撑,而是在脆弱与恐惧面前,依然愿意温柔而坚定地活下去。',
intro: '你最近可能已经耗费了不少精神去「撑住一切」。这张牌提醒你:真正的力量,允许自己偶尔放下盔甲。',
tips: [
'列出一件你最近「勉强自己」去做的事,问问自己:如果不再逞强,我还有没有别的选择?',
'给自己安排一个真正意义上的休息,而不是只是换一种形式的忙碌。',
'当你向可信任的人示弱时,那不是失败,而是一种对生命的信任。'
]
}
];
function getMoonPhaseText() {
const now = new Date();
const lp = 2551443;
const newMoon = new Date(1970, 0, 7, 20, 35, 0);
const phase = ((now.getTime() - newMoon.getTime()) / 1000) % lp;
const phaseIndex = Math.floor((phase / lp) * 8);
const phases = [
'新月 · 适合种下一个新的念头',
'娥眉月 · 小小的行动就足够',
'上弦月 · 给自己一个明确选择',
'盈凸月 · 事情逐渐清晰起来',
'满月 · 情绪与答案都被照亮',
'亏凸月 · 学会放下和整理',
'下弦月 · 为下一次出发做准备',
'残月 · 对自己温柔一点'
];
return phases[phaseIndex] || '月亮正在为你保留一个安静的夜晚';
}
function randomFrom(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function setMeter(target) {
meterFill.style.width = target + '%';
}
function openSheet(sheet) {
overlay.classList.add('visible');
sheet.classList.add('visible');
sheet.setAttribute('aria-hidden', 'false');
}
function closeSheet(sheet) {
sheet.classList.remove('visible');
sheet.setAttribute('aria-hidden', 'true');
if (!sheetResult.classList.contains('visible') &&
!sheetHistory.classList.contains('visible') &&
!sheetGuide.classList.contains('visible')) {
overlay.classList.remove('visible');
}
}
function renderResult(card, layoutLabel) {
const orientation = Math.random() < 0.75 ? '正位' : '逆位';
const intro = card.intro;
const summary = card.summary;
currentResult = {
id: card.id,
name: card.name,
type: card.type,
orientation,
short: card.short,
layout: layoutLabel,
ts: Date.now()
};
resultHeading.textContent = card.name + ' · ' + (orientation === '正位' ? '正位' : '逆位');
resultSymbol.textContent = card.symbol;
resultShort.textContent = card.short;
resultType.textContent = card.type;
resultOrientation.textContent = orientation;
resultName.textContent = card.name + '(' + card.short + ')';
resultPositionLabel.textContent = '此牌正在回应「' + layoutLabel + '」';
resultIntro.textContent = intro;
resultSummary.textContent = summary;
resultTips.innerHTML = '';
card.tips.forEach(t => {
const li = document.createElement('li');
li.textContent = t;
resultTips.appendChild(li);
});
}
function loadHistory() {
const raw = localStorage.getItem('moon-arcana-history');
if (!raw) return [];
try {
const list = JSON.parse(raw);
if (Array.isArray(list)) return list;
} catch (_) {}
return [];
}
function saveHistory() {
if (!currentResult) return;
const list = loadHistory();
list.unshift(currentResult);
const trimmed = list.slice(0, 5);
localStorage.setItem('moon-arcana-history', JSON.stringify(trimmed));
renderHistory(trimmed);
}
function renderHistory(list) {
historyList.innerHTML = '';
if (!list.length) {
const item = document.createElement('div');
item.className = 'history-item';
item.innerHTML = '<div class="history-main"><div>暂无记录</div><div class="history-meta">首次使用时,我们会在这里为你保留最近的牌面。</div></div><div class="history-pill">Empty</div>';
historyList.appendChild(item);
return;
}
list.forEach(entry => {
const date = new Date(entry.ts);
const label = date.toLocaleString('zh-CN', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
const item = document.createElement('div');
item.className = 'history-item';
item.innerHTML = `
<div class="history-main">
<div>${entry.name} · ${entry.orientation}</div>
<div class="history-meta">${label} · 布局:${entry.layout}</div>
</div>
<div class="history-pill">${entry.type === '大阿尔卡纳' ? 'Major' : 'Minor'}</div>
`;
item.addEventListener('click', () => {
const card = TAROT_POOL.find(c => c.id === entry.id);
if (!card) return;
renderResult(card, entry.layout);
openSheet(sheetResult);
});
historyList.appendChild(item);
});
}
function resetDeck() {
hasDrawn = false;
isShuffling = false;
spreadStage.classList.remove('is-shuffling');
cards.forEach((card, index) => {
card.classList.remove('is-drawn');
card.style.transform = '';
card.style.opacity = '';
void card.offsetWidth;
if (index === 0) {
card.style.transform = 'translate3d(0,0,0) rotate(0deg)';
} else if (index === 1) {
card.style.transform = 'translate3d(1px,2px,-10px) rotate(-1deg)';
card.style.opacity = '0.97';
} else if (index === 2) {
card.style.transform = 'translate3d(-1px,4px,-20px) rotate(1deg)';
card.style.opacity = '0.94';
}
});
deckMessage.textContent = '请将注意力停留在一个具体的问题上。';
setMeter(45);
}
function startShuffle() {
if (isShuffling) return;
isShuffling = true;
spreadStage.classList.add('is-shuffling');
deckMessage.textContent = '轻轻触摸牌堆,或点击「开始抽牌」,当你感觉刚刚好,就在那一刻停下。';
setMeter(60);
cards.forEach((card, index) => {
const baseDelay = index * 100;
const animate = () => {
if (!isShuffling || hasDrawn) return;
const offsetX = (Math.random() - 0.5) * 6;
const offsetY = (Math.random() - 0.5) * 6;
const rotate = (Math.random() - 0.5) * 4;
card.style.transform = `translate3d(${offsetX}px, ${offsetY}px, ${index * -10}px) rotate(${rotate}deg)`;
card.style.opacity = 0.9 + (3 - index) * 0.03;
setTimeout(animate, 700 + index * 120);
};
setTimeout(animate, baseDelay + 150);
});
}
function drawCard() {
if (hasDrawn) {
openSheet(sheetResult);
return;
}
if (!isShuffling) {
startShuffle();
}
hasDrawn = true;
isShuffling = false;
spreadStage.classList.remove('is-shuffling');
deckMessage.textContent = '这张牌正在为你发声。缓一缓,再点击下方结果。';
setMeter(85);
const targetCard = randomFrom(cards);
cards.forEach(card => {
card.classList.remove('is-hovered');
if (card !== targetCard) {
card.style.opacity = '0.18';
card.style.transform += ' translate3d(0,10px,-40px) scale(0.92)';
}
});
targetCard.classList.add('is-drawn');
setTimeout(() => {
const picked = randomFrom(TAROT_POOL);
const layoutLabel = currentLayout === 'single' ? '当下心境' : '过去 / 现在 / 未来的交汇点';
renderResult(picked, layoutLabel);
saveHistory();
openSheet(sheetResult);
}, 700);
}
cards.forEach(card => {
card.addEventListener('pointerenter', () => {
if (hasDrawn) return;
card.classList.add('is-hovered');
});
card.addEventListener('pointerleave', () => {
card.classList.remove('is-hovered');
});
card.addEventListener('click', () => drawCard());
card.addEventListener('touchstart', (e) => {
e.preventDefault();
drawCard();
}, { passive: false });
});
btnDraw.addEventListener('click', () => {
if (!isShuffling && !hasDrawn) {
startShuffle();
setTimeout(drawCard, 900);
} else {
drawCard();
}
});
btnReset.addEventListener('click', () => {
resetDeck();
});
btnHistory.addEventListener('click', () => {
renderHistory(loadHistory());
openSheet(sheetHistory);
btnHistory.classList.add('active');
setTimeout(() => btnHistory.classList.remove('active'), 260);
});
btnGuide.addEventListener('click', () => {
openSheet(sheetGuide);
btnGuide.classList.add('active');
setTimeout(() => btnGuide.classList.remove('active'), 260);
});
overlay.addEventListener('click', () => {
[sheetResult, sheetHistory, sheetGuide].forEach(closeSheet);
});
document.querySelectorAll('.sheet-close').forEach(btn => {
btn.addEventListener('click', () => {
const target = btn.getAttribute('data-close-sheet');
if (target === 'result') closeSheet(sheetResult);
if (target === 'history') closeSheet(sheetHistory);
if (target === 'guide') closeSheet(sheetGuide);
});
});
layoutPills.forEach(pill => {
pill.addEventListener('click', () => {
layoutPills.forEach(p => p.classList.remove('active'));
pill.classList.add('active');
currentLayout = pill.getAttribute('data-layout') || 'single';
if (currentLayout === 'single') {
deckMessage.textContent = '请将注意力停留在一个具体的问题上。';
setMeter(45);
document.getElementById('insightMood').textContent = '情绪起伏略大,适合用一张牌安静地照亮内心。';
} else {
deckMessage.textContent = '过去、现在、未来交织在一起,请带着耐心看清时间给你的答案。';
setMeter(52);
document.getElementById('insightMood').textContent = '你似乎正在回顾一段长线故事,这个布局会帮你看见时间的脉络。';
}
});
});
function init() {
resetDeck();
const history = loadHistory();
renderHistory(history);
moonPhaseEl.textContent = getMoonPhaseText();
}
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>
对应上面《使用对比》中的2:一遍生成,无任何修改
html<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>秘境塔罗 · Tarot H5</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
font-family: "STHeiti", "PingFang SC", "Hiragino Sans GB", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: radial-gradient(circle at top, #1e1035 0, #05020a 55%, #010005 100%);
color: #f7f3ff;
-webkit-font-smoothing: antialiased;
overflow: hidden;
}
:root {
--accent: #ffce6a;
--accent-soft: rgba(255, 206, 106, 0.1);
--accent-strong: #ff9f4a;
--danger: #ff4d7a;
--card-bg: rgba(12, 7, 30, 0.96);
--card-border: rgba(255, 255, 255, 0.06);
--muted: #9c8ac7;
--muted-soft: rgba(156, 138, 199, 0.16);
--surface: rgba(10, 4, 24, 0.9);
--radius-lg: 22px;
--radius-md: 14px;
--blur-strong: 32px;
--page-padding: 18px;
}
body::before {
content: "";
position: fixed;
inset: -40%;
background:
radial-gradient(circle at 20% 0%, rgba(255, 231, 153, 0.18), transparent 55%),
radial-gradient(circle at 90% 10%, rgba(139, 92, 246, 0.22), transparent 60%),
radial-gradient(circle at 0% 80%, rgba(56, 189, 248, 0.12), transparent 55%);
mix-blend-mode: screen;
opacity: 0.75;
pointer-events: none;
z-index: -1;
}
.app-shell {
position: relative;
max-width: 480px;
height: 100vh;
margin: 0 auto;
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
display: flex;
flex-direction: column;
}
.app-frame {
flex: 1;
margin: 10px var(--page-padding) 6px;
background: linear-gradient(145deg, rgba(15, 10, 38, 0.98), rgba(3, 2, 15, 0.98));
border-radius: 28px;
box-shadow:
0 25px 80px rgba(0, 0, 0, 0.85),
0 0 0 1px rgba(255, 255, 255, 0.04);
overflow: hidden;
position: relative;
backdrop-filter: blur(var(--blur-strong));
border: 1px solid rgba(185, 148, 255, 0.18);
}
.app-chrome {
position: absolute;
inset: 0;
pointer-events: none;
border-radius: inherit;
background:
radial-gradient(circle at 10% 0%, rgba(255, 255, 255, 0.16), transparent 65%),
radial-gradient(circle at 100% 100%, rgba(255, 148, 255, 0.16), transparent 60%);
mix-blend-mode: soft-light;
opacity: 0.6;
}
.app-content {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 16px 16px 10px;
}
.app-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.app-title-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.app-logo-word {
font-size: 13px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.6);
}
.app-title {
font-size: 20px;
letter-spacing: 0.08em;
font-weight: 600;
display: inline-flex;
align-items: baseline;
gap: 6px;
}
.app-title span {
background: linear-gradient(120deg, #ffe39a, #ffc46e, #ff9f4a);
-webkit-background-clip: text;
color: transparent;
}
.app-title-sub {
font-size: 11px;
color: var(--muted);
}
.app-actions {
display: flex;
align-items: center;
gap: 8px;
}
.icon-btn {
width: 28px;
height: 28px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 65%), rgba(8, 4, 22, 0.9);
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--muted);
font-size: 15px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.7);
backdrop-filter: blur(14px);
cursor: pointer;
transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease, color 0.16s ease;
}
.icon-btn:active {
transform: translateY(1px) scale(0.98);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
}
.icon-btn--primary {
border-color: rgba(255, 206, 106, 0.5);
color: var(--accent);
}
.app-main {
flex: 1;
position: relative;
overflow: hidden;
border-radius: 20px;
background: radial-gradient(circle at top, rgba(71, 45, 143, 0.96), rgba(14, 5, 37, 0.96));
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.65);
padding: 14px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.page {
position: absolute;
inset: 0;
padding: 14px 12px 14px;
display: flex;
flex-direction: column;
gap: 14px;
opacity: 0;
pointer-events: none;
transform: translateX(6%);
transition: opacity 0.28s ease, transform 0.28s ease;
}
.page--active {
opacity: 1;
pointer-events: auto;
transform: translateX(0);
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.page-title-group {
display: flex;
flex-direction: column;
gap: 3px;
}
.page-tagline {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.12em;
text-transform: uppercase;
}
.page-title {
font-size: 18px;
letter-spacing: 0.06em;
display: flex;
align-items: center;
gap: 8px;
}
.page-title span {
background: linear-gradient(120deg, #ffe6aa, #ffbf67, #ff9c4b);
-webkit-background-clip: text;
color: transparent;
}
.page-title-badge {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 60%), rgba(9, 3, 26, 0.9);
color: var(--muted);
}
.page-desc {
font-size: 12px;
line-height: 1.6;
color: rgba(234, 224, 255, 0.92);
}
.page-desc strong {
color: var(--accent);
}
.pill-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin-top: 2px;
}
.pill {
font-size: 11px;
padding: 3px 9px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(13, 6, 33, 0.9);
color: var(--muted);
display: inline-flex;
align-items: center;
gap: 4px;
}
.pill-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: radial-gradient(circle, var(--accent), rgba(255, 206, 106, 0.1));
}
.card-shell {
border-radius: var(--radius-lg);
padding: 10px 10px 8px;
background:
radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 65%),
linear-gradient(140deg, rgba(19, 8, 48, 0.97), rgba(17, 4, 42, 0.98));
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.9),
0 0 0 1px rgba(255, 255, 255, 0.06);
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
overflow: hidden;
}
.card-shell::before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(115deg, rgba(255, 230, 182, 0.1), transparent 50%, rgba(156, 138, 199, 0.1)),
repeating-linear-gradient(135deg, rgba(95, 68, 155, 0.1), rgba(95, 68, 155, 0.1) 1px, transparent 1px, transparent 3px);
mix-blend-mode: soft-light;
opacity: 0.7;
pointer-events: none;
}
.hero-row {
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 10px;
align-items: stretch;
}
.hero-card {
border-radius: 20px;
background:
radial-gradient(circle at 15% 0%, rgba(255, 255, 255, 0.35), transparent 65%),
radial-gradient(circle at 100% 100%, rgba(252, 211, 77, 0.5), transparent 60%),
linear-gradient(145deg, #1a0828, #11071f);
padding: 9px 9px 11px;
box-shadow:
0 18px 45px rgba(0, 0, 0, 0.95),
0 0 0 1px rgba(255, 255, 255, 0.16);
position: relative;
overflow: hidden;
}
.hero-orbit {
position: absolute;
inset: 10px 18px 18px;
border-radius: 18px;
border: 1px dashed rgba(255, 255, 255, 0.18);
opacity: 0.75;
}
.hero-orbit::before,
.hero-orbit::after {
content: "";
position: absolute;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.22);
opacity: 0.4;
}
.hero-orbit::before {
inset: 12px 6px;
}
.hero-orbit::after {
inset: 6px 18px;
}
.hero-card-inner {
position: relative;
z-index: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 10px;
}
.hero-card-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.hero-chip {
font-size: 11px;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.24);
background: rgba(5, 2, 14, 0.8);
color: rgba(255, 255, 255, 0.78);
display: inline-flex;
align-items: center;
gap: 4px;
}
.hero-chip-dot {
width: 6px;
height: 6px;
border-radius: 999px;
background: radial-gradient(circle, var(--accent), rgba(255, 255, 255, 0.1));
box-shadow: 0 0 8px rgba(255, 228, 181, 0.9);
}
.hero-constellation {
width: 26px;
height: 26px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.24);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
background: radial-gradient(circle at 30% 0%, rgba(255, 255, 255, 0.4), transparent 60%), rgba(4, 2, 15, 0.9);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.9);
}
.hero-signature {
text-align: center;
padding-top: 4px;
}
.hero-signature-main {
font-size: 14px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.92);
}
.hero-signature-sub {
font-size: 11px;
margin-top: 2px;
color: rgba(243, 244, 246, 0.8);
}
.hero-figure {
position: relative;
margin-top: 6px;
height: 72px;
}
.hero-moon {
position: absolute;
top: 4px;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
background: radial-gradient(circle at 30% 15%, #fff7d1, #f7c26c 65%, #e7873e 100%);
box-shadow:
0 0 30px rgba(255, 252, 210, 0.85),
0 0 80px rgba(253, 224, 171, 0.7);
}
.hero-moon::after {
content: "";
position: absolute;
inset: 4px 2px;
border-radius: 50%;
background: radial-gradient(circle at 10% 20%, #fffbe8, transparent 55%);
mix-blend-mode: screen;
}
.hero-rays {
position: absolute;
inset: 12px 10px 0;
border-radius: 36px;
border: 1px dashed rgba(255, 255, 255, 0.55);
opacity: 0.65;
}
.hero-stars {
position: absolute;
inset: 10px 8px 0;
pointer-events: none;
}
.hero-stars span {
position: absolute;
width: 3px;
height: 3px;
border-radius: 50%;
background: radial-gradient(circle, #fff, rgba(255, 255, 255, 0));
box-shadow: 0 0 8px rgba(255, 255, 255, 0.9);
}
.hero-stars span:nth-child(1) { top: 6px; left: 18%; }
.hero-stars span:nth-child(2) { top: 2px; right: 26%; }
.hero-stars span:nth-child(3) { top: 28px; left: 8%; }
.hero-stars span:nth-child(4) { top: 34px; right: 12%; }
.hero-stars span:nth-child(5) { bottom: 10px; left: 26%; }
.hero-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 3px;
font-size: 11px;
color: rgba(229, 231, 235, 0.95);
}
.hero-footer span {
display: inline-flex;
align-items: center;
gap: 4px;
}
.hero-footer small {
color: rgba(229, 231, 235, 0.6);
}
.hero-metadata {
border-radius: 18px;
background: rgba(5, 1, 21, 0.9);
border: 1px solid rgba(226, 232, 255, 0.09);
padding: 9px 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 10px;
position: relative;
overflow: hidden;
}
.hero-metadata::before {
content: "";
position: absolute;
inset: -20%;
background:
radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 65%),
radial-gradient(circle at 100% 90%, rgba(79, 70, 229, 0.42), transparent 65%);
opacity: 0.8;
filter: blur(4px);
}
.hero-metadata-inner {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.meta-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.meta-row strong {
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.meta-row span {
font-size: 11px;
color: rgba(209, 213, 219, 0.75);
}
.progress-track {
position: relative;
width: 100%;
height: 6px;
border-radius: 999px;
background: rgba(19, 14, 47, 0.95);
overflow: hidden;
border: 1px solid rgba(148, 163, 184, 0.3);
}
.progress-bar {
position: absolute;
inset: 0;
width: 72%;
background: linear-gradient(90deg, var(--accent), var(--accent-strong), #ff7b7b);
box-shadow: 0 0 14px rgba(250, 204, 21, 0.9);
}
.meta-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.meta-tag {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.75);
border: 1px solid rgba(148, 163, 184, 0.48);
color: rgba(226, 232, 255, 0.9);
display: inline-flex;
align-items: center;
gap: 4px;
}
.meta-tag-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: linear-gradient(135deg, #4ade80, #22c55e);
box-shadow: 0 0 10px rgba(74, 222, 128, 0.9);
}
.section-caption {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 2px;
}
.section-caption-left {
font-size: 11px;
color: var(--muted);
display: flex;
align-items: center;
gap: 6px;
}
.section-divider {
flex: 1;
height: 1px;
border-radius: 999px;
background: linear-gradient(90deg, rgba(107, 114, 128, 0.1), rgba(255, 255, 255, 0.4), rgba(107, 114, 128, 0.1));
opacity: 0.8;
}
.mode-row {
display: flex;
gap: 8px;
}
.mode-card {
flex: 1;
border-radius: var(--radius-md);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 60%), rgba(8, 3, 26, 0.92);
border: 1px solid rgba(148, 163, 184, 0.35);
padding: 7px 8px 8px;
display: flex;
flex-direction: column;
gap: 4px;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, background 0.15s ease;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.9);
}
.mode-card:hover {
transform: translateY(-1px);
box-shadow: 0 16px 40px rgba(15, 23, 42, 1);
border-color: rgba(249, 250, 251, 0.4);
background: radial-gradient(circle at 5% 0, rgba(248, 250, 252, 0.28), transparent 55%), rgba(8, 3, 26, 0.96);
}
.mode-card--primary {
border-color: rgba(252, 211, 77, 0.8);
background: radial-gradient(circle at 0 0, rgba(255, 243, 199, 0.28), transparent 55%), rgba(24, 16, 45, 0.96);
}
.mode-label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
}
.mode-name {
font-size: 13px;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.mode-badge {
font-size: 10px;
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.45);
color: rgba(209, 213, 219, 0.9);
}
.mode-desc {
font-size: 11px;
color: rgba(209, 213, 219, 0.9);
line-height: 1.55;
}
.primary-btn {
width: 100%;
margin-top: 8px;
border-radius: 999px;
border: none;
padding: 10px 14px;
font-size: 15px;
letter-spacing: 0.18em;
text-transform: uppercase;
font-weight: 600;
background: linear-gradient(120deg, #fbbf24, #fb923c, #f97316);
color: #1f2937;
box-shadow:
0 18px 40px rgba(248, 181, 0, 0.9),
0 0 0 1px rgba(255, 237, 213, 0.9);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: transform 0.16s ease, box-shadow 0.16s ease, filter 0.16s ease;
}
.primary-btn span {
position: relative;
z-index: 1;
}
.primary-btn::before {
content: "";
position: absolute;
inset: -40%;
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.75), transparent 60%);
opacity: 0;
transform: translateX(-40%);
transition: transform 0.4s ease, opacity 0.4s ease;
}
.primary-btn:hover::before {
opacity: 1;
transform: translateX(40%);
}
.primary-btn:active {
transform: translateY(1px) scale(0.985);
box-shadow:
0 10px 30px rgba(248, 181, 0, 0.9),
0 0 0 1px rgba(251, 146, 60, 0.8);
filter: brightness(0.98);
}
.secondary-link {
margin-top: 4px;
text-align: center;
font-size: 11px;
color: var(--muted);
}
.secondary-link button {
border: none;
background: none;
color: rgba(248, 250, 252, 0.86);
text-decoration: underline;
text-underline-offset: 3px;
cursor: pointer;
padding: 0 2px;
font-size: 11px;
}
.spread-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-top: 4px;
}
.spread-card {
border-radius: 14px;
background: rgba(15, 23, 42, 0.96);
padding: 6px 6px 7px;
border: 1px solid rgba(75, 85, 99, 0.9);
display: flex;
flex-direction: column;
gap: 4px;
cursor: pointer;
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.95);
transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease, background 0.14s ease;
}
.spread-card:hover {
transform: translateY(-1px);
border-color: rgba(249, 250, 251, 0.7);
background: radial-gradient(circle at 0 0, rgba(248, 250, 252, 0.25), transparent 55%), rgba(15, 23, 42, 0.96);
box-shadow: 0 16px 40px rgba(15, 23, 42, 1);
}
.spread-card--active {
border-color: var(--accent);
box-shadow:
0 16px 40px rgba(248, 181, 0, 0.85),
0 0 0 1px rgba(251, 191, 36, 0.6);
background: radial-gradient(circle at 0 0, rgba(255, 237, 213, 0.3), transparent 60%), rgba(15, 23, 42, 0.98);
}
.spread-visual {
height: 42px;
border-radius: 11px;
background: repeating-linear-gradient(135deg, rgba(31, 41, 55, 0.9), rgba(31, 41, 55, 0.9) 2px, rgba(17, 24, 39, 0.9) 2px, rgba(17, 24, 39, 0.9) 4px);
overflow: hidden;
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 2px;
padding: 0 4px;
}
.spread-visual span {
display: block;
border-radius: 6px;
border: 1px solid rgba(156, 163, 175, 0.9);
background: linear-gradient(145deg, #020617, #020617);
width: 16px;
height: 32px;
}
.spread-visual--single span {
width: 26px;
height: 36px;
border-radius: 7px;
}
.spread-name {
font-size: 12px;
font-weight: 500;
color: rgba(249, 250, 251, 0.96);
}
.spread-meta {
font-size: 10px;
color: rgba(209, 213, 219, 0.8);
}
.flow-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 3px;
font-size: 11px;
color: rgba(209, 213, 219, 0.8);
}
.flow-steps {
display: flex;
align-items: center;
gap: 4px;
}
.flow-dot {
width: 10px;
height: 10px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(156, 163, 175, 0.8);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 7px;
}
.flow-dot--active {
background: linear-gradient(135deg, #fbbf24, #f97316);
border-color: rgba(250, 204, 21, 0.9);
color: #1f2937;
box-shadow: 0 0 10px rgba(250, 204, 21, 0.9);
}
.table-layout {
flex: 1;
border-radius: 18px;
background:
radial-gradient(circle at 10% 0, rgba(31, 41, 55, 0.8), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(15, 23, 42, 0.95), transparent 60%);
border: 1px solid rgba(148, 163, 184, 0.5);
margin-top: 2px;
padding: 10px 10px 10px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
}
.table-layout::before {
content: "";
position: absolute;
inset: 18px 22px;
border-radius: 18px;
border: 1px dashed rgba(229, 231, 235, 0.45);
opacity: 0.5;
}
.deck-area {
position: relative;
width: 78%;
max-width: 260px;
height: 140px;
perspective: 1400px;
}
.tarot-card {
position: absolute;
inset: auto;
left: 50%;
bottom: 0;
width: 76px;
height: 118px;
margin-left: -38px;
border-radius: 12px;
background: linear-gradient(145deg, #020617, #020617);
border: 1px solid rgba(209, 213, 219, 0.85);
box-shadow:
0 18px 40px rgba(15, 23, 42, 0.95),
0 0 0 1px rgba(17, 24, 39, 1);
transform-origin: center bottom;
transform-style: preserve-3d;
cursor: pointer;
overflow: hidden;
}
.tarot-card-back {
position: absolute;
inset: 3px;
border-radius: 9px;
background:
radial-gradient(circle at 0 0, rgba(248, 250, 252, 0.35), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(209, 213, 219, 0.55), transparent 65%),
repeating-linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 0.9) 2px, rgba(15, 23, 42, 1) 2px, rgba(15, 23, 42, 1) 4px);
display: flex;
align-items: center;
justify-content: center;
}
.tarot-card-sigil {
width: 72%;
height: 56%;
border-radius: 999px;
border: 1px solid rgba(249, 250, 251, 0.8);
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.tarot-card-sigil::before,
.tarot-card-sigil::after {
content: "";
position: absolute;
border-radius: inherit;
border: 1px solid rgba(249, 250, 251, 0.7);
}
.tarot-card-sigil::before {
inset: 4px 10px;
}
.tarot-card-sigil::after {
inset: 10px 4px;
}
.tarot-symbol {
position: relative;
width: 26px;
height: 26px;
border-radius: 50%;
border: 1px solid rgba(249, 250, 251, 0.9);
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.tarot-symbol::before,
.tarot-symbol::after {
content: "";
position: absolute;
inset: 2px;
border-radius: inherit;
border: 1px dashed rgba(249, 250, 251, 0.7);
}
.tarot-symbol::after {
inset: 6px;
border-style: solid;
opacity: 0.7;
}
.deck-shadow {
position: absolute;
left: 50%;
bottom: -3px;
width: 62%;
height: 18px;
margin-left: -31%;
background: radial-gradient(circle at 50% 0, rgba(0, 0, 0, 0.85), transparent 75%);
filter: blur(4px);
opacity: 0.9;
pointer-events: none;
}
.deck-hint {
font-size: 11px;
color: rgba(209, 213, 219, 0.85);
text-align: center;
margin-top: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.deck-hint span {
font-size: 13px;
}
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 11px;
color: rgba(209, 213, 219, 0.8);
margin-top: 2px;
}
.table-footer-left {
display: inline-flex;
align-items: center;
gap: 6px;
}
.mini-indicator {
padding: 2px 7px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.6);
background: rgba(15, 23, 42, 0.85);
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 10px;
}
.mini-indicator-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: linear-gradient(135deg, #22c55e, #16a34a);
box-shadow: 0 0 10px rgba(34, 197, 94, 0.9);
}
.bottom-nav {
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 999px;
background: rgba(9, 5, 27, 0.96);
border: 1px solid rgba(148, 163, 184, 0.45);
padding: 6px 8px;
margin-top: 10px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.95);
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
font-size: 10px;
color: rgba(156, 163, 175, 0.9);
padding: 4px 0;
border-radius: 999px;
cursor: pointer;
transition: background 0.16s ease, color 0.16s ease, transform 0.16s ease;
}
.nav-item-icon {
font-size: 15px;
}
.nav-item--active {
background: radial-gradient(circle at 50% 0, rgba(253, 224, 171, 0.4), transparent 55%), rgba(15, 23, 42, 0.98);
color: var(--accent);
transform: translateY(-1px);
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.9);
}
.page-scroll {
flex: 1;
overflow-y: auto;
padding-right: 4px;
margin-right: -4px;
}
.page-scroll::-webkit-scrollbar {
width: 4px;
}
.page-scroll::-webkit-scrollbar-track {
background: transparent;
}
.page-scroll::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.6);
border-radius: 999px;
}
.history-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
}
.history-item {
border-radius: 14px;
background: rgba(15, 23, 42, 0.92);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 8px 9px;
display: flex;
gap: 8px;
font-size: 11px;
}
.history-thumb {
width: 34px;
height: 54px;
border-radius: 8px;
border: 1px solid rgba(156, 163, 175, 0.85);
background: linear-gradient(145deg, #020617, #020617);
display: flex;
align-items: center;
justify-content: center;
color: rgba(249, 250, 251, 0.9);
font-size: 10px;
}
.history-meta {
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
}
.history-title-row {
display: flex;
justify-content: space-between;
gap: 6px;
font-size: 11px;
color: rgba(249, 250, 251, 0.96);
}
.history-sub {
font-size: 10px;
color: rgba(209, 213, 219, 0.8);
}
.history-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 2px;
}
.history-tag {
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.7);
color: rgba(209, 213, 219, 0.9);
font-size: 10px;
}
.settings-group {
border-radius: 16px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 8px 9px;
display: flex;
flex-direction: column;
gap: 6px;
margin-top: 4px;
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.setting-label {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 11px;
color: rgba(249, 250, 251, 0.96);
}
.setting-label span {
font-size: 10px;
color: rgba(156, 163, 175, 0.9);
}
.toggle {
width: 40px;
height: 22px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 2px;
display: flex;
align-items: center;
cursor: pointer;
transition: background 0.18s ease, border-color 0.18s ease;
}
.toggle-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: rgba(148, 163, 184, 1);
box-shadow: 0 1px 6px rgba(15, 23, 42, 0.9);
transition: transform 0.18s ease, background 0.18s ease;
}
.toggle--on {
background: linear-gradient(135deg, #22c55e, #16a34a);
border-color: rgba(34, 197, 94, 0.8);
}
.toggle--on .toggle-thumb {
transform: translateX(16px);
background: #f9fafb;
}
.pill-select {
display: inline-flex;
align-items: center;
gap: 4px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.92);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 3px 6px 3px 5px;
font-size: 10px;
color: rgba(209, 213, 219, 0.9);
}
.pill-select select {
border: none;
background: transparent;
color: inherit;
font-size: inherit;
outline: none;
}
.rules-list {
display: flex;
flex-direction: column;
gap: 5px;
margin-top: 4px;
font-size: 11px;
color: rgba(209, 213, 219, 0.92);
}
.rules-list li {
display: flex;
gap: 6px;
}
.rules-list li::before {
content: "✶";
color: var(--accent);
font-size: 9px;
margin-top: 2px;
}
.result-layout {
flex: 1;
border-radius: 18px;
background:
radial-gradient(circle at 10% 0, rgba(31, 41, 55, 0.8), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(15, 23, 42, 0.95), transparent 60%);
border: 1px solid rgba(148, 163, 184, 0.5);
margin-top: 2px;
padding: 10px;
display: grid;
grid-template-columns: 0.9fr 1.1fr;
gap: 10px;
}
.result-card-shell {
border-radius: 16px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 6px 6px 8px;
display: flex;
flex-direction: column;
gap: 5px;
align-items: center;
}
.result-card {
width: 88px;
height: 142px;
border-radius: 13px;
background: linear-gradient(145deg, #facc15, #f97316);
padding: 4px;
box-shadow:
0 20px 45px rgba(248, 181, 0, 0.85),
0 0 0 1px rgba(255, 248, 220, 0.9);
position: relative;
overflow: hidden;
}
.result-card-inner {
position: absolute;
inset: 4px;
border-radius: 10px;
background:
radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.45), transparent 50%),
radial-gradient(circle at 100% 0, rgba(251, 191, 36, 0.4), transparent 55%),
linear-gradient(145deg, #1f2937, #020617);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 6px 4px 6px;
color: #f9fafb;
}
.result-card-name {
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.result-card-figure {
width: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.result-orbit {
width: 56px;
height: 56px;
border-radius: 999px;
border: 1px dashed rgba(249, 250, 251, 0.8);
display: flex;
align-items: center;
justify-content: center;
}
.result-orbit::before {
content: "";
position: absolute;
width: 42px;
height: 42px;
border-radius: inherit;
border: 1px solid rgba(249, 250, 251, 0.8);
opacity: 0.5;
}
.result-glyph {
width: 24px;
height: 24px;
border-radius: 50%;
background: radial-gradient(circle at 30% 0, #fefce8, #fbbf24);
display: flex;
align-items: center;
justify-content: center;
color: #1f2937;
font-size: 14px;
box-shadow: 0 0 18px rgba(250, 204, 21, 0.9);
}
.result-card-bottom {
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgba(243, 244, 246, 0.8);
}
.result-meta {
text-align: center;
font-size: 11px;
color: rgba(209, 213, 219, 0.9);
display: flex;
flex-direction: column;
gap: 3px;
}
.result-meta strong {
color: var(--accent);
}
.result-content {
border-radius: 14px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 7px 8px;
display: flex;
flex-direction: column;
gap: 5px;
font-size: 11px;
color: rgba(209, 213, 219, 0.96);
}
.result-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 2px;
}
.result-tag {
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.9);
font-size: 10px;
}
.result-section-title {
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: rgba(156, 163, 175, 0.95);
}
.result-paragraph {
font-size: 11px;
line-height: 1.6;
}
.result-actions {
margin-top: 4px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 11px;
}
.ghost-btn {
flex: 1;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.9);
background: rgba(15, 23, 42, 0.96);
padding: 6px 8px;
color: rgba(249, 250, 251, 0.96);
font-size: 11px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
transition: background 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease;
}
.ghost-btn:hover {
background: radial-gradient(circle at 0 0, rgba(248, 250, 252, 0.2), transparent 55%), rgba(15, 23, 42, 0.98);
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.9);
transform: translateY(-1px);
}
.ghost-btn:active {
transform: translateY(0);
box-shadow: 0 5px 18px rgba(15, 23, 42, 0.9);
}
.app-toast {
position: absolute;
left: 50%;
bottom: 12px;
transform: translateX(-50%) translateY(120%);
padding: 7px 12px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.98);
border: 1px solid rgba(148, 163, 184, 0.9);
color: rgba(249, 250, 251, 0.96);
font-size: 11px;
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.9);
opacity: 0;
pointer-events: none;
transition: opacity 0.32s ease, transform 0.32s ease;
z-index: 20;
}
.app-toast--visible {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.app-toast span {
font-size: 13px;
}
@keyframes cardShuffle {
0% { transform: translateX(0) translateY(0) rotate(0deg); }
30% { transform: translateX(-10px) translateY(-12px) rotate(-6deg); }
60% { transform: translateX(12px) translateY(-6px) rotate(5deg); }
100% { transform: translateX(0) translateY(0) rotate(0deg); }
}
@keyframes cardDraw {
0% { transform: translateX(0) translateY(0) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translateX(-40px) translateY(-60px) rotate(-10deg) scale(1.05); opacity: 0; }
}
@keyframes pulseGlow {
0%, 100% { box-shadow: 0 0 16px rgba(250, 204, 21, 0.6); }
50% { box-shadow: 0 0 26px rgba(250, 204, 21, 0.9); }
}
.deck-area--shuffling .tarot-card {
animation: cardShuffle 0.8s ease-in-out infinite;
}
.deck-area--drawing .tarot-card {
animation: cardDraw 0.6s ease-in-out forwards;
}
@media (max-width: 360px) {
.app-frame {
margin: 4px var(--page-padding) 4px;
border-radius: 24px;
}
.hero-row {
grid-template-columns: 1fr;
}
.result-layout {
grid-template-columns: 1fr;
grid-auto-rows: auto;
}
}
</style>
</head>
<body>
<div class="app-shell">
<div class="app-frame">
<div class="app-chrome"></div>
<div class="app-content">
<header class="app-header">
<div class="app-title-group">
<div class="app-logo-word">ARCANA STUDIO</div>
<div class="app-title">
<span>秘境塔罗</span>
<span class="app-title-sub">H5 体验版</span>
</div>
</div>
<div class="app-actions">
<button class="icon-btn" data-page-jump="help" title="使用说明">?</button>
<button class="icon-btn icon-btn--primary" data-page-jump="result" title="最近一次解读">★</button>
</div>
</header>
<main class="app-main">
<section class="page page--active" data-page="home">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">DAILY TAROT • 今日灵感</div>
<div class="page-title">
<span>开始一次新的占卜</span>
<div class="page-title-badge">Mood · 心境校准</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="card-shell">
<div class="hero-row">
<div class="hero-card">
<div class="hero-orbit"></div>
<div class="hero-card-inner">
<div class="hero-card-top">
<div class="hero-chip">
<span class="hero-chip-dot"></span>
<span>今日主牌</span>
</div>
<div class="hero-constellation">♓</div>
</div>
<div class="hero-signature">
<div class="hero-signature-main">ARCANA FLOW</div>
<div class="hero-signature-sub">Shuffle. Draw. Listen.</div>
</div>
<div class="hero-figure">
<div class="hero-moon"></div>
<div class="hero-rays"></div>
<div class="hero-stars">
<span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<div class="hero-footer">
<span>直觉指数 <small>Intuition</small></span>
<span>灵感在线 <small>在线抽牌</small></span>
</div>
</div>
</div>
<aside class="hero-metadata">
<div class="hero-metadata-inner">
<div class="meta-row">
<strong>今日频率</strong>
<span id="meta-date-label">•••</span>
</div>
<div class="progress-track">
<div class="progress-bar"></div>
</div>
<div class="meta-tags">
<div class="meta-tag">
<span class="meta-tag-dot"></span>
心灵指引
</div>
<div class="meta-tag">3~5 分钟完成</div>
<div class="meta-tag">适合 · 日常问题</div>
</div>
</div>
</aside>
</div>
<div class="section-caption">
<div class="section-caption-left">
<span>选择一种抽牌方式</span>
</div>
<div class="section-divider"></div>
</div>
<div class="mode-row">
<button class="mode-card mode-card--primary" data-spread-select="single">
<div class="mode-label">
<div class="mode-name">单牌指引</div>
<div class="mode-badge">入门 · 快速</div>
</div>
<div class="mode-desc">适合「今天应该聚焦什么?」这类轻量问题,获得一条清晰的指引。</div>
</button>
<button class="mode-card" data-spread-select="triple">
<div class="mode-label">
<div class="mode-name">三牌流向</div>
<div class="mode-badge">进阶 · 深入</div>
</div>
<div class="mode-desc">同时看见过去、现在与下一步,理解你所在的完整情境。</div>
</button>
</div>
<button class="primary-btn" data-action="go-spread">
<span>开始洗牌</span>
<span>⟶</span>
</button>
<div class="secondary-link">
想先了解玩法?<button data-page-jump="help">查看塔罗礼仪与注意事项</button>
</div>
</section>
</div>
</section>
<section class="page" data-page="spread">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">SPREAD · 阵列选择</div>
<div class="page-title">
<span>选择你的牌阵结构</span>
<div class="page-title-badge" id="spread-mode-label">默认:单牌指引</div>
</div>
</div>
<button class="icon-btn" data-page-jump="home">←</button>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
每一次抽牌,都从一个清晰的
<strong>问题</strong> 开始。请在心里默念你的问题,并选择一种你此刻最被吸引的牌阵。
</p>
<div class="spread-grid">
<button class="spread-card spread-card--active" data-spread-option="single">
<div class="spread-visual spread-visual--single">
<span></span>
</div>
<div class="spread-name">单牌指引</div>
<div class="spread-meta">一次抽出一张,给出当下最需要听见的讯息。</div>
</button>
<button class="spread-card" data-spread-option="triple">
<div class="spread-visual">
<span></span><span></span><span></span>
</div>
<div class="spread-name">三牌流向</div>
<div class="spread-meta">过去 / 现在 / 下一步,适合关系、规划与决策。</div>
</button>
<button class="spread-card" data-spread-option="mirror">
<div class="spread-visual">
<span></span><span></span>
</div>
<div class="spread-name">镜像自我</div>
<div class="spread-meta">看见「我以为的自己」与「内在真实的自己」。</div>
</button>
</div>
<div class="flow-row">
<div class="flow-steps">
<div class="flow-dot flow-dot--active">1</div>
<span>选择牌阵</span>
<div class="flow-dot">2</div>
<span>洗牌 · 抽牌</span>
<div class="flow-dot">3</div>
<span>解读结果</span>
</div>
<div>当前预计用时 < 5 分钟</div>
</div>
</section>
<section class="table-layout">
<div class="deck-area" id="deck-area">
<div class="tarot-card" id="tarot-card">
<div class="tarot-card-back">
<div class="tarot-card-sigil">
<div class="tarot-symbol">✶</div>
</div>
</div>
</div>
<div class="deck-shadow"></div>
</div>
<div class="deck-hint">
<span>⇵</span>
用手指轻轻「模拟洗牌动作」,准备好后,<strong>点击牌堆</strong> 开始抽牌。
</div>
<div class="table-footer">
<div class="table-footer-left">
<span>问题已锁定 · 请保持心中专注</span>
</div>
<div class="mini-indicator">
<span class="mini-indicator-dot"></span>
在线连接 · 安全私密
</div>
</div>
</section>
</div>
</section>
<section class="page" data-page="result">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">INTERPRETATION · 解读</div>
<div class="page-title">
<span>本次抽到的主牌</span>
<div class="page-title-badge" id="result-spread-label">单牌指引 · Demo</div>
</div>
</div>
<button class="icon-btn" data-page-jump="spread">←</button>
</div>
<div class="page-scroll">
<section class="result-layout">
<div class="result-card-shell">
<div class="result-card">
<div class="result-card-inner">
<div class="result-card-name" id="result-card-name">THE SUN</div>
<div class="result-card-figure">
<div class="result-orbit">
<div class="result-glyph">☼</div>
</div>
</div>
<div class="result-card-bottom" id="result-card-orientation">正位 · SUN</div>
</div>
</div>
<div class="result-meta">
<span>本页面为 <strong>UI 原型示例</strong>,用于展示塔罗结果的排版方式。</span>
<span>真实业务中可根据后端数据填充牌名、正逆位、关键词与详细解读。</span>
</div>
</div>
<div class="result-content">
<div class="result-section-title">核心关键词</div>
<div class="result-tags" id="result-tags">
<span class="result-tag">清晰</span>
<span class="result-tag">坦诚</span>
<span class="result-tag">能量回归</span>
<span class="result-tag">信任自己</span>
</div>
<div class="result-section-title">讯息解读</div>
<p class="result-paragraph" id="result-text-main">
你正从一段相对混沌的阶段走向更清晰的状态。这张牌邀请你<strong>相信自己的判断</strong>,而不是不断向外索取答案。
你已经具备足够的信息与经验,只是还需要一点点勇气,为自己做出一个「真诚的选择」。
</p>
<p class="result-paragraph">
在接下来的几天里,可以刻意安排一些
<strong>让你真正感到轻松、开心</strong> 的小事,例如晒太阳、整理空间、与信任的人聊聊。
这些看似琐碎的行动,会帮助你更快走出旧的循环,迎接新的可能。
</p>
<div class="result-actions">
<button class="ghost-btn" data-action="save-history">
<span>存为一次占卜记录</span>
</button>
<button class="ghost-btn" data-action="redo">
<span>重新抽一轮</span>
</button>
</div>
</div>
</section>
</div>
</section>
<section class="page" data-page="history">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">ARCHIVE · 记录</div>
<div class="page-title">
<span>我的塔罗时间线</span>
<div class="page-title-badge">Demo · UI 展示</div>
</div>
</div>
<button class="icon-btn" data-page-jump="home">←</button>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
这里会展示你每一次抽牌的
<strong>时间、主题与核心讯息</strong>。当前版本以示例数据展示布局,方便后续和后端对齐字段。
</p>
<div class="history-list" id="history-list">
<article class="history-item">
<div class="history-thumb">SUN</div>
<div class="history-meta">
<div class="history-title-row">
<div>2026-03-16 · 单牌指引</div>
<div>上午 10:23</div>
</div>
<div class="history-sub">主题:关于工作选择的直觉提示</div>
<div class="history-tags">
<span class="history-tag">牌面:太阳 · 正位</span>
<span class="history-tag">结论:更靠近让你发光的场域</span>
</div>
</div>
</article>
<article class="history-item">
<div class="history-thumb">HER</div>
<div class="history-meta">
<div class="history-title-row">
<div>2026-03-15 · 三牌流向</div>
<div>晚上 21:07</div>
</div>
<div class="history-sub">主题:关于一段亲密关系的进展</div>
<div class="history-tags">
<span class="history-tag">主牌:女祭司 · 逆位</span>
<span class="history-tag">建议:诚实表达而非揣测</span>
</div>
</div>
</article>
</div>
</section>
<section class="settings-group">
<div class="setting-row">
<div class="setting-label">
<div>记录同步</div>
<span>视觉上展示是否将记录同步到云端(真实逻辑可接后端)。</span>
</div>
<div class="toggle" data-toggle="sync">
<div class="toggle-thumb"></div>
</div>
</div>
<div class="setting-row">
<div class="setting-label">
<div>对外分享</div>
<span>允许将解读结果以图片形式分享(后续可接分享能力)。</span>
</div>
<div class="pill-select">
<span>方式</span>
<select>
<option>仅自己可见</option>
<option>图片分享</option>
<option>链接分享</option>
</select>
</div>
</div>
</section>
</div>
</section>
<section class="page" data-page="help">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">GUIDE · 使用说明</div>
<div class="page-title">
<span>如何与塔罗好好相处</span>
<div class="page-title-badge">界面文案示例</div>
</div>
</div>
<button class="icon-btn" data-page-jump="home">←</button>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
塔罗是一种帮助你
<strong>看见自己</strong> 的工具,而不是预言命运。本页面用于展示
<strong>引导文案</strong> 与
<strong>说明信息的 UI 排版</strong>。
</p>
<ol class="rules-list">
<li>在开始抽牌前,为这次提问设定一个清晰的主题,例如「我和某段关系接下来会如何发展?」。</li>
<li>在洗牌与抽牌过程中,尽量让自己专注在问题本身,而不是急着寻找某个「好牌」。</li>
<li>看到结果时,先观察自己的第一反应:身体是放松、紧绷,还是有某种画面跳出来?这些都是讯息的一部分。</li>
<li>如果对某次解读感到困惑,可以在「记录」中回看几天前的抽牌,有时答案会在时间中慢慢对齐。</li>
<li>本应用的 UI/UX 仅为演示,具体业务可根据项目需要接入账号体系、付费模块或更复杂的解读逻辑。</li>
</ol>
</section>
</div>
</section>
</main>
<nav class="bottom-nav">
<button class="nav-item nav-item--active" data-page-jump="home">
<div class="nav-item-icon">◎</div>
<div>首页</div>
</button>
<button class="nav-item" data-page-jump="spread">
<div class="nav-item-icon">♧</div>
<div>抽牌</div>
</button>
<button class="nav-item" data-page-jump="result">
<div class="nav-item-icon">✶</div>
<div>结果</div>
</button>
<button class="nav-item" data-page-jump="history">
<div class="nav-item-icon">☷</div>
<div>记录</div>
</button>
</nav>
<div class="app-toast" id="app-toast">
<span>✶</span>
<span id="toast-message">已保存到「记录」示例列表。</span>
</div>
</div>
</div>
</div>
<script>
(function () {
const pages = document.querySelectorAll(".page");
const navItems = document.querySelectorAll(".nav-item");
const pageJumpButtons = document.querySelectorAll("[data-page-jump]");
const spreadModeLabel = document.getElementById("spread-mode-label");
const resultSpreadLabel = document.getElementById("result-spread-label");
const metaDateLabel = document.getElementById("meta-date-label");
const deckArea = document.getElementById("deck-area");
const tarotCard = document.getElementById("tarot-card");
const toast = document.getElementById("app-toast");
const toastMessage = document.getElementById("toast-message");
const toggles = document.querySelectorAll(".toggle");
const spreadCards = document.querySelectorAll(".spread-card");
const modeCards = document.querySelectorAll(".mode-card");
let currentPage = "home";
let currentSpread = "single";
let isShuffling = false;
function showPage(target) {
if (!target || target === currentPage) return;
currentPage = target;
pages.forEach((page) => {
const name = page.getAttribute("data-page");
page.classList.toggle("page--active", name === target);
});
navItems.forEach((item) => {
const jump = item.getAttribute("data-page-jump");
item.classList.toggle("nav-item--active", jump === target);
});
}
function showToast(message) {
if (!toast) return;
toastMessage.textContent = message;
toast.classList.add("app-toast--visible");
window.clearTimeout(showToast._timer);
showToast._timer = window.setTimeout(() => {
toast.classList.remove("app-toast--visible");
}, 1900);
}
function updateSpreadLabels() {
const labelMap = {
single: "单牌指引",
triple: "三牌流向",
mirror: "镜像自我"
};
const text = labelMap[currentSpread] || "自定义牌阵";
if (spreadModeLabel) {
spreadModeLabel.textContent = "当前:" + text;
}
if (resultSpreadLabel) {
resultSpreadLabel.textContent = text + " · Demo";
}
}
function initDateLabel() {
try {
const now = new Date();
const days = ["日", "一", "二", "三", "四", "五", "六"];
const label = now.getFullYear() + " / " + (now.getMonth() + 1) + " / " + now.getDate() + " · 周" + days[now.getDay()];
if (metaDateLabel) {
metaDateLabel.textContent = label;
}
} catch (e) {}
}
pageJumpButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const target = btn.getAttribute("data-page-jump");
showPage(target);
});
});
if (deckArea && tarotCard) {
deckArea.addEventListener("click", () => {
if (isShuffling) return;
isShuffling = true;
deckArea.classList.add("deck-area--drawing");
window.setTimeout(() => {
deckArea.classList.remove("deck-area--drawing");
isShuffling = false;
showPage("result");
showToast("已为你生成一张示例牌面。");
}, 650);
});
deckArea.addEventListener("touchstart", () => {
deckArea.classList.add("deck-area--shuffling");
});
deckArea.addEventListener("touchend", () => {
deckArea.classList.remove("deck-area--shuffling");
});
deckArea.addEventListener("touchcancel", () => {
deckArea.classList.remove("deck-area--shuffling");
});
}
document.querySelectorAll("[data-action='go-spread']").forEach((btn) => {
btn.addEventListener("click", () => {
showPage("spread");
});
});
document.querySelectorAll("[data-action='save-history']").forEach((btn) => {
btn.addEventListener("click", () => {
showToast("示例:已保存到「记录」列表。");
});
});
document.querySelectorAll("[data-action='redo']").forEach((btn) => {
btn.addEventListener("click", () => {
showPage("spread");
showToast("你可以带着新问题再抽一次。");
});
});
toggles.forEach((toggle) => {
toggle.addEventListener("click", () => {
toggle.classList.toggle("toggle--on");
});
});
spreadCards.forEach((card) => {
card.addEventListener("click", () => {
const option = card.getAttribute("data-spread-option");
if (!option) return;
currentSpread = option;
spreadCards.forEach((c) => c.classList.toggle("spread-card--active", c === card));
updateSpreadLabels();
showToast("已选择牌阵:「" + (card.querySelector(".spread-name")?.textContent || "") + "」。");
});
});
modeCards.forEach((card) => {
card.addEventListener("click", () => {
const selectKey = card.getAttribute("data-spread-select");
if (selectKey) {
currentSpread = selectKey;
updateSpreadLabels();
}
modeCards.forEach((c) => c.classList.toggle("mode-card--primary", c === card));
});
});
initDateLabel();
updateSpreadLabels();
})();
</script>
</body>
</html>
对应上面《使用对比》中的3:提示词重复沟通3遍后:
html<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>秘境塔罗 · H5 UI 原型</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "SF Pro Display", "Segoe UI", system-ui, sans-serif;
background: radial-gradient(circle at top, #1e1035 0, #05020a 55%, #010005 100%);
color: #f7f3ff;
-webkit-font-smoothing: antialiased;
}
:root {
--accent: #ffce6a;
--accent-strong: #ff9f4a;
--muted: #a6a0c8;
--surface: rgba(10, 4, 24, 0.92);
--border-subtle: rgba(255, 255, 255, 0.08);
--radius-lg: 22px;
--radius-md: 14px;
}
body::before {
content: "";
position: fixed;
inset: -40%;
background:
radial-gradient(circle at 20% 0%, rgba(255, 231, 153, 0.18), transparent 55%),
radial-gradient(circle at 90% 10%, rgba(139, 92, 246, 0.22), transparent 60%),
radial-gradient(circle at 0% 80%, rgba(56, 189, 248, 0.12), transparent 55%);
mix-blend-mode: screen;
opacity: 0.75;
pointer-events: none;
z-index: -1;
}
.app-shell {
max-width: 480px;
height: 100vh;
margin: 0 auto;
padding: env(safe-area-inset-top) 16px env(safe-area-inset-bottom);
display: flex;
flex-direction: column;
}
.app-frame {
flex: 1;
margin: 10px 0 6px;
border-radius: 28px;
background: linear-gradient(145deg, rgba(15, 10, 38, 0.98), rgba(3, 2, 15, 0.98));
box-shadow:
0 25px 80px rgba(0, 0, 0, 0.85),
0 0 0 1px rgba(255, 255, 255, 0.04);
overflow: hidden;
position: relative;
}
.app-chrome {
position: absolute;
inset: 0;
pointer-events: none;
border-radius: inherit;
background:
radial-gradient(circle at 10% 0%, rgba(255, 255, 255, 0.16), transparent 65%),
radial-gradient(circle at 100% 100%, rgba(255, 148, 255, 0.16), transparent 60%);
mix-blend-mode: soft-light;
opacity: 0.6;
}
.app-content {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 16px 14px 10px;
}
.app-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.app-title-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.app-logo-word {
font-size: 13px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.6);
}
.app-title {
font-size: 20px;
letter-spacing: 0.08em;
font-weight: 600;
display: inline-flex;
align-items: baseline;
gap: 6px;
}
.app-title span {
background: linear-gradient(120deg, #ffe39a, #ffc46e, #ff9f4a);
-webkit-background-clip: text;
color: transparent;
}
.app-title-sub {
font-size: 11px;
color: var(--muted);
}
.app-actions {
display: flex;
align-items: center;
gap: 8px;
}
.icon-btn {
width: 28px;
height: 28px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 65%), rgba(8, 4, 22, 0.9);
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--muted);
font-size: 15px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.7);
cursor: pointer;
border-radius: 999px;
}
.icon-btn--primary {
border-color: rgba(255, 206, 106, 0.5);
color: var(--accent);
}
.app-main {
flex: 1;
position: relative;
overflow: hidden;
border-radius: 20px;
background: radial-gradient(circle at top, rgba(71, 45, 143, 0.96), rgba(14, 5, 37, 0.96));
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.65);
padding: 14px 12px;
display: flex;
flex-direction: column;
}
.page {
position: absolute;
inset: 0;
padding: 14px 12px 14px;
display: flex;
flex-direction: column;
gap: 14px;
opacity: 0;
pointer-events: none;
transform: translateX(6%);
transition: opacity 0.28s ease, transform 0.28s ease;
}
.page--active {
opacity: 1;
pointer-events: auto;
transform: translateX(0);
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.page-title-group {
display: flex;
flex-direction: column;
gap: 3px;
}
.page-tagline {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.12em;
text-transform: uppercase;
}
.page-title {
font-size: 18px;
letter-spacing: 0.06em;
display: flex;
align-items: center;
gap: 8px;
}
.page-title span {
background: linear-gradient(120deg, #ffe6aa, #ffbf67, #ff9c4b);
-webkit-background-clip: text;
color: transparent;
}
.page-title-badge {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 60%), rgba(9, 3, 26, 0.9);
color: var(--muted);
}
.page-desc {
font-size: 12px;
line-height: 1.6;
color: rgba(234, 224, 255, 0.92);
}
.page-desc strong {
color: var(--accent);
}
.page-scroll {
flex: 1;
overflow-y: auto;
padding-right: 4px;
margin-right: -4px;
}
.page-scroll::-webkit-scrollbar { width: 4px; }
.page-scroll::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.6);
border-radius: 999px;
}
.card-shell {
border-radius: var(--radius-lg);
padding: 10px 10px 8px;
background:
radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 65%),
linear-gradient(140deg, rgba(19, 8, 48, 0.97), rgba(17, 4, 42, 0.98));
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.9),
0 0 0 1px rgba(255, 255, 255, 0.06);
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
overflow: hidden;
}
.hero-row {
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 10px;
align-items: stretch;
}
.hero-card {
border-radius: 20px;
background:
radial-gradient(circle at 15% 0%, rgba(255, 255, 255, 0.35), transparent 65%),
radial-gradient(circle at 100% 100%, rgba(252, 211, 77, 0.5), transparent 60%),
linear-gradient(145deg, #1a0828, #11071f);
padding: 9px 9px 11px;
box-shadow:
0 18px 45px rgba(0, 0, 0, 0.95),
0 0 0 1px rgba(255, 255, 255, 0.16);
position: relative;
overflow: hidden;
}
.hero-card-inner {
position: relative;
z-index: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 10px;
}
.hero-signature {
text-align: center;
padding-top: 4px;
}
.hero-signature-main {
font-size: 14px;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.hero-signature-sub {
font-size: 11px;
margin-top: 2px;
color: rgba(243, 244, 246, 0.8);
}
.hero-figure {
position: relative;
margin-top: 6px;
height: 72px;
}
.hero-moon {
position: absolute;
top: 4px;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
background: radial-gradient(circle at 30% 15%, #fff7d1, #f7c26c 65%, #e7873e 100%);
box-shadow:
0 0 30px rgba(255, 252, 210, 0.85),
0 0 80px rgba(253, 224, 171, 0.7);
}
.section-caption {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 2px;
font-size: 11px;
color: var(--muted);
}
.section-divider {
flex: 1;
height: 1px;
border-radius: 999px;
background: linear-gradient(90deg, rgba(107, 114, 128, 0.1), rgba(255, 255, 255, 0.4), rgba(107, 114, 128, 0.1));
opacity: 0.8;
}
.mode-row {
display: flex;
gap: 8px;
}
.mode-card {
flex: 1;
border-radius: var(--radius-md);
background: radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.2), transparent 60%), rgba(8, 3, 26, 0.92);
border: 1px solid rgba(148, 163, 184, 0.35);
padding: 7px 8px 8px;
display: flex;
flex-direction: column;
gap: 4px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.9);
font-size: 11px;
}
.mode-card--primary {
border-color: rgba(252, 211, 77, 0.8);
background: radial-gradient(circle at 0 0, rgba(255, 243, 199, 0.28), transparent 55%), rgba(24, 16, 45, 0.96);
}
.mode-label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
}
.mode-name {
font-size: 13px;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.mode-badge {
font-size: 10px;
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.45);
color: rgba(209, 213, 219, 0.9);
}
.mode-desc {
font-size: 11px;
color: rgba(209, 213, 219, 0.9);
line-height: 1.55;
}
.primary-btn {
width: 100%;
margin-top: 8px;
border-radius: 999px;
border: none;
padding: 10px 14px;
font-size: 15px;
letter-spacing: 0.18em;
text-transform: uppercase;
font-weight: 600;
background: linear-gradient(120deg, #fbbf24, #fb923c, #f97316);
color: #1f2937;
box-shadow:
0 18px 40px rgba(248, 181, 0, 0.9),
0 0 0 1px rgba(255, 237, 213, 0.9);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.secondary-link {
margin-top: 4px;
text-align: center;
font-size: 11px;
color: var(--muted);
}
.secondary-link span {
color: #f9fafb;
text-decoration: underline;
text-underline-offset: 3px;
}
.spread-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-top: 4px;
}
.spread-card {
border-radius: 14px;
background: rgba(15, 23, 42, 0.96);
padding: 6px 6px 7px;
border: 1px solid rgba(75, 85, 99, 0.9);
display: flex;
flex-direction: column;
gap: 4px;
font-size: 10px;
}
.spread-card--active {
border-color: var(--accent);
box-shadow:
0 16px 40px rgba(248, 181, 0, 0.85),
0 0 0 1px rgba(251, 191, 36, 0.6);
background: radial-gradient(circle at 0 0, rgba(255, 237, 213, 0.3), transparent 60%), rgba(15, 23, 42, 0.98);
}
.spread-visual {
height: 40px;
border-radius: 11px;
background: repeating-linear-gradient(135deg, rgba(31, 41, 55, 0.9), rgba(31, 41, 55, 0.9) 2px, rgba(17, 24, 39, 0.9) 2px, rgba(17, 24, 39, 0.9) 4px);
display: flex;
align-items: center;
justify-content: center;
gap: 2px;
padding: 0 4px;
}
.spread-visual span {
display: block;
border-radius: 6px;
border: 1px solid rgba(156, 163, 175, 0.9);
background: linear-gradient(145deg, #020617, #020617);
width: 16px;
height: 30px;
}
.spread-visual--single span {
width: 26px;
height: 34px;
border-radius: 7px;
}
.spread-name {
font-size: 12px;
font-weight: 500;
color: rgba(249, 250, 251, 0.96);
}
.spread-meta {
font-size: 10px;
color: rgba(209, 213, 219, 0.8);
}
.table-layout {
flex: 1;
border-radius: 18px;
background:
radial-gradient(circle at 10% 0, rgba(31, 41, 55, 0.8), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(15, 23, 42, 0.95), transparent 60%);
border: 1px solid rgba(148, 163, 184, 0.5);
margin-top: 2px;
padding: 10px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
}
.deck-area {
position: relative;
width: 78%;
max-width: 260px;
height: 140px;
}
.tarot-card {
position: absolute;
left: 50%;
bottom: 0;
width: 76px;
height: 118px;
margin-left: -38px;
border-radius: 12px;
background: linear-gradient(145deg, #020617, #020617);
border: 1px solid rgba(209, 213, 219, 0.85);
box-shadow:
0 18px 40px rgba(15, 23, 42, 0.95),
0 0 0 1px rgba(17, 24, 39, 1);
overflow: hidden;
}
.tarot-card-back {
position: absolute;
inset: 3px;
border-radius: 9px;
background:
radial-gradient(circle at 0 0, rgba(248, 250, 252, 0.35), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(209, 213, 219, 0.55), transparent 65%),
repeating-linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 0.9) 2px, rgba(15, 23, 42, 1) 2px, rgba(15, 23, 42, 1) 4px);
display: flex;
align-items: center;
justify-content: center;
}
.tarot-card-sigil {
width: 72%;
height: 56%;
border-radius: 999px;
border: 1px solid rgba(249, 250, 251, 0.8);
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.tarot-symbol {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(249, 250, 251, 0.9);
font-size: 14px;
}
.deck-hint {
font-size: 11px;
color: rgba(209, 213, 219, 0.85);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.deck-shadow {
position: absolute;
left: 50%;
bottom: -3px;
width: 62%;
height: 18px;
margin-left: -31%;
background: radial-gradient(circle at 50% 0, rgba(0, 0, 0, 0.85), transparent 75%);
filter: blur(4px);
opacity: 0.9;
pointer-events: none;
}
/* 抽卡动效 */
@keyframes cardShuffle {
0% { transform: translateX(0) translateY(0) rotate(0deg); }
30% { transform: translateX(-10px) translateY(-12px) rotate(-6deg); }
60% { transform: translateX(12px) translateY(-6px) rotate(5deg); }
100% { transform: translateX(0) translateY(0) rotate(0deg); }
}
@keyframes cardDraw {
0% { transform: translateX(0) translateY(0) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translateX(-40px) translateY(-60px) rotate(-10deg) scale(1.05); opacity: 0; }
}
.deck-area--shuffling .tarot-card {
animation: cardShuffle 0.8s ease-in-out infinite;
}
.deck-area--drawing .tarot-card {
animation: cardDraw 0.7s ease-in-out forwards;
}
.result-layout {
flex: 1;
border-radius: 18px;
background:
radial-gradient(circle at 10% 0, rgba(31, 41, 55, 0.8), transparent 60%),
radial-gradient(circle at 100% 100%, rgba(15, 23, 42, 0.95), transparent 60%);
border: 1px solid rgba(148, 163, 184, 0.5);
margin-top: 2px;
padding: 10px;
display: grid;
grid-template-columns: 0.9fr 1.1fr;
gap: 10px;
}
.result-card-shell {
border-radius: 16px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 6px 6px 8px;
display: flex;
flex-direction: column;
gap: 5px;
align-items: center;
}
.result-card {
width: 88px;
height: 142px;
border-radius: 13px;
background: linear-gradient(145deg, #facc15, #f97316);
padding: 4px;
box-shadow:
0 20px 45px rgba(248, 181, 0, 0.85),
0 0 0 1px rgba(255, 248, 220, 0.9);
position: relative;
overflow: hidden;
}
.result-card-inner {
position: absolute;
inset: 4px;
border-radius: 10px;
background:
radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.45), transparent 50%),
radial-gradient(circle at 100% 0, rgba(251, 191, 36, 0.4), transparent 55%),
linear-gradient(145deg, #1f2937, #020617);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 6px 4px 6px;
color: #f9fafb;
}
.result-card-name {
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.result-card-figure {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.result-orbit {
width: 56px;
height: 56px;
border-radius: 999px;
border: 1px dashed rgba(249, 250, 251, 0.8);
display: flex;
align-items: center;
justify-content: center;
}
.result-glyph {
width: 24px;
height: 24px;
border-radius: 50%;
background: radial-gradient(circle at 30% 0, #fefce8, #fbbf24);
display: flex;
align-items: center;
justify-content: center;
color: #1f2937;
font-size: 14px;
}
.result-card-bottom {
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgba(243, 244, 246, 0.8);
}
.result-content {
border-radius: 14px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 7px 8px;
display: flex;
flex-direction: column;
gap: 5px;
font-size: 11px;
color: rgba(209, 213, 219, 0.96);
}
.result-section-title {
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: rgba(156, 163, 175, 0.95);
}
.result-paragraph {
font-size: 11px;
line-height: 1.6;
}
.result-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 2px;
}
.result-tag {
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.9);
font-size: 10px;
}
.result-actions {
margin-top: 4px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 11px;
}
.ghost-btn {
flex: 1;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.9);
background: rgba(15, 23, 42, 0.96);
padding: 6px 8px;
color: rgba(249, 250, 251, 0.96);
font-size: 11px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.history-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
font-size: 11px;
}
.history-item {
border-radius: 14px;
background: rgba(15, 23, 42, 0.92);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 8px 9px;
display: flex;
gap: 8px;
}
.history-thumb {
width: 34px;
height: 54px;
border-radius: 8px;
border: 1px solid rgba(156, 163, 175, 0.85);
background: linear-gradient(145deg, #020617, #020617);
display: flex;
align-items: center;
justify-content: center;
color: rgba(249, 250, 251, 0.9);
font-size: 10px;
}
.history-meta {
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
}
.history-title-row {
display: flex;
justify-content: space-between;
gap: 6px;
font-size: 11px;
color: rgba(249, 250, 251, 0.96);
}
.history-sub {
font-size: 10px;
color: rgba(209, 213, 219, 0.8);
}
.history-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 2px;
}
.history-tag {
padding: 1px 6px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.7);
color: rgba(209, 213, 219, 0.9);
font-size: 10px;
}
.settings-group {
border-radius: 16px;
background: rgba(15, 23, 42, 0.96);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 8px 9px;
display: flex;
flex-direction: column;
gap: 6px;
margin-top: 4px;
font-size: 11px;
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.setting-label {
display: flex;
flex-direction: column;
gap: 2px;
}
.setting-label span {
font-size: 10px;
color: rgba(156, 163, 175, 0.9);
}
.toggle {
width: 40px;
height: 22px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(75, 85, 99, 0.9);
padding: 2px;
display: flex;
align-items: center;
}
.toggle-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: rgba(148, 163, 184, 1);
}
.rules-list {
display: flex;
flex-direction: column;
gap: 5px;
margin-top: 4px;
font-size: 11px;
color: rgba(209, 213, 219, 0.92);
}
.rules-list li {
display: flex;
gap: 6px;
}
.rules-list li::before {
content: "✶";
color: var(--accent);
font-size: 9px;
margin-top: 2px;
}
.bottom-nav {
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 999px;
background: rgba(9, 5, 27, 0.96);
border: 1px solid rgba(148, 163, 184, 0.45);
padding: 6px 8px;
margin-top: 10px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.95);
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
font-size: 10px;
color: rgba(156, 163, 175, 0.9);
padding: 4px 0;
border-radius: 999px;
}
.nav-item-icon {
font-size: 15px;
}
.nav-item--active {
background: radial-gradient(circle at 50% 0, rgba(253, 224, 171, 0.4), transparent 55%), rgba(15, 23, 42, 0.98);
color: var(--accent);
}
.app-toast {
position: absolute;
left: 50%;
bottom: 12px;
transform: translateX(-50%);
padding: 7px 12px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.98);
border: 1px solid rgba(148, 163, 184, 0.9);
color: rgba(249, 250, 251, 0.96);
font-size: 11px;
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.9);
opacity: 0;
pointer-events: none;
transform-origin: center;
transition: opacity 0.32s ease;
}
.app-toast--visible {
opacity: 1;
}
@media (max-width: 360px) {
.app-frame {
margin: 4px 0 4px;
border-radius: 24px;
}
.hero-row {
grid-template-columns: 1fr;
}
.result-layout {
grid-template-columns: 1fr;
grid-auto-rows: auto;
}
}
</style>
</head>
<body>
<div class="app-shell">
<div class="app-frame">
<div class="app-chrome"></div>
<div class="app-content">
<header class="app-header">
<div class="app-title-group">
<div class="app-logo-word">ARCANA STUDIO</div>
<div class="app-title">
<span>秘境塔罗</span>
<span class="app-title-sub">H5 UI 原型</span>
</div>
</div>
<div class="app-actions">
<button class="icon-btn" data-page-jump="help" title="使用说明">?</button>
<button class="icon-btn icon-btn--primary" data-page-jump="result" title="结果页面">★</button>
</div>
</header>
<main class="app-main">
<!-- 首页:问题设定 & 入口 -->
<section class="page page--active" data-page="home">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">DAILY TAROT • 今日灵感</div>
<div class="page-title">
<span>开始一次新的占卜</span>
<div class="page-title-badge">Step 1 · 设定问题</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="card-shell">
<div class="hero-row">
<div class="hero-card">
<div class="hero-card-inner">
<div class="hero-signature">
<div class="hero-signature-main">ARCANA FLOW</div>
<div class="hero-signature-sub">Shuffle · Draw · Listen</div>
</div>
<div class="hero-figure">
<div class="hero-moon"></div>
</div>
</div>
</div>
<aside class="card-shell" style="gap:6px; padding:8px 9px;">
<p class="page-desc">
在心里为这次占卜设定一个<strong>清晰的问题</strong>,例如「我今天应该把注意力放在什么上?」。
</p>
<p class="page-desc">
右下角底部导航展示了本原型包含的所有页面:<strong>首页 / 抽牌 / 结果 / 记录 / 说明</strong>,方便产品与设计评审。
</p>
</aside>
</div>
<div class="section-caption">
<span>选择一种抽牌方式</span>
<div class="section-divider"></div>
</div>
<div class="mode-row">
<div class="mode-card mode-card--primary">
<div class="mode-label">
<div class="mode-name">单牌指引</div>
<div class="mode-badge">入门 · 快速</div>
</div>
<div class="mode-desc">适合「今天应该聚焦什么?」这类问题,卡片结果页面会展示核心关键词与短文解读。</div>
</div>
<div class="mode-card">
<div class="mode-label">
<div class="mode-name">三牌流向</div>
<div class="mode-badge">进阶 · 深入</div>
</div>
<div class="mode-desc">过去 / 现在 / 下一步 的布局可在结果页扩展为三列卡片与三段解读。</div>
</div>
</div>
<button class="primary-btn" data-page-jump="spread">
<span>开始洗牌</span>
<span>⟶</span>
</button>
<div class="secondary-link">
想先了解玩法?<span>查看塔罗礼仪与注意事项(见「说明」页)</span>
</div>
</section>
</div>
</section>
<!-- 抽牌页:牌阵选择 + 洗牌交互区域 -->
<section class="page" data-page="spread">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">SPREAD · 阵列选择</div>
<div class="page-title">
<span>选择你的牌阵结构</span>
<div class="page-title-badge">Step 2 · 选择牌阵</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
每一次抽牌,从一个清晰的<strong>主题</strong>和<strong>合适的牌阵</strong>开始。下方展示了 3 种典型配置,可根据业务需要增删。
</p>
<div class="spread-grid">
<div class="spread-card spread-card--active">
<div class="spread-visual spread-visual--single"><span></span></div>
<div class="spread-name">单牌指引</div>
<div class="spread-meta">一次抽出一张牌,快速获得一个当下的方向提示。</div>
</div>
<div class="spread-card">
<div class="spread-visual">
<span></span><span></span><span></span>
</div>
<div class="spread-name">三牌流向</div>
<div class="spread-meta">左:过去 · 中:现在 · 右:下一步,适合关系、决策类问题。</div>
</div>
<div class="spread-card">
<div class="spread-visual">
<span></span><span></span>
</div>
<div class="spread-name">镜像自我</div>
<div class="spread-meta">看见「我以为的自己」与「内在真实的自己」。</div>
</div>
</div>
</section>
<section class="table-layout">
<div class="deck-area">
<div class="tarot-card">
<div class="tarot-card-back">
<div class="tarot-card-sigil">
<div class="tarot-symbol">✶</div>
</div>
</div>
</div>
<div class="deck-shadow"></div>
</div>
<div class="deck-hint">
<span>⇵</span>
在真实产品中,这里可接「拖动 / 点击」交互逻辑;原型中仅展示牌堆视觉与提示文案。
</div>
</section>
</div>
</section>
<!-- 结果页:主牌展示 + 文案区 -->
<section class="page" data-page="result">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">INTERPRETATION · 解读</div>
<div class="page-title">
<span>本次抽到的主牌</span>
<div class="page-title-badge">Step 3 · 结果展示</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="result-layout">
<div class="result-card-shell">
<div class="result-card">
<div class="result-card-inner">
<div class="result-card-name">THE SUN</div>
<div class="result-card-figure">
<div class="result-orbit">
<div class="result-glyph">☼</div>
</div>
</div>
<div class="result-card-bottom">正位 · SUN</div>
</div>
</div>
<div class="page-desc" style="font-size:11px; margin-top:4px;">
卡片名称、正逆位、元素归属等信息可由后端返回;本原型仅展示视觉与文案承载结构。
</div>
</div>
<div class="result-content">
<div class="result-section-title">核心关键词</div>
<div class="result-tags">
<span class="result-tag">清晰</span>
<span class="result-tag">坦诚</span>
<span class="result-tag">能量回归</span>
<span class="result-tag">信任自己</span>
</div>
<div class="result-section-title">讯息解读</div>
<p class="result-paragraph">
你正从一段相对混沌的阶段走向更清晰的状态。这张牌邀请你<strong>相信自己的判断</strong>,而不是不断向外索取答案。
你已经具备足够的信息与经验,只是还需要一点点勇气,为自己做出一个「真诚的选择」。
</p>
<p class="result-paragraph">
在接下来的几天里,可以刻意安排一些<strong>让你真正感到轻松、开心</strong>的小事,例如晒太阳、整理空间、与信任的人聊聊。
这些看似琐碎的行动,会帮助你更快走出旧的循环,迎接新的可能。
</p>
<div class="result-actions">
<button class="ghost-btn">
<span>存为一次占卜记录</span>
</button>
<button class="ghost-btn">
<span>重新抽一轮</span>
</button>
</div>
</div>
</section>
</div>
</section>
<!-- 记录页:历史抽牌时间线 -->
<section class="page" data-page="history">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">ARCHIVE · 记录</div>
<div class="page-title">
<span>我的塔罗时间线</span>
<div class="page-title-badge">Demo · UI 展示</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
这里展示每一次抽牌的<strong>时间 / 主题 / 关键词</strong>。示例数据仅用于说明排版,后续可接入登录与云同步。
</p>
<div class="history-list">
<article class="history-item">
<div class="history-thumb">SUN</div>
<div class="history-meta">
<div class="history-title-row">
<div>2026-03-16 · 单牌指引</div>
<div>上午 10:23</div>
</div>
<div class="history-sub">主题:关于工作选择的直觉提示</div>
<div class="history-tags">
<span class="history-tag">牌面:太阳 · 正位</span>
<span class="history-tag">结论:更靠近让你发光的场域</span>
</div>
</div>
</article>
<article class="history-item">
<div class="history-thumb">HER</div>
<div class="history-meta">
<div class="history-title-row">
<div>2026-03-15 · 三牌流向</div>
<div>晚上 21:07</div>
</div>
<div class="history-sub">主题:关于一段亲密关系的进展</div>
<div class="history-tags">
<span class="history-tag">主牌:女祭司 · 逆位</span>
<span class="history-tag">建议:诚实表达而非揣测</span>
</div>
</div>
</article>
</div>
</section>
<section class="settings-group">
<div class="setting-row">
<div class="setting-label">
<div>记录同步</div>
<span>视觉上展示「是否同步到云端」,真实逻辑可与后端对齐。</span>
</div>
<div class="toggle">
<div class="toggle-thumb"></div>
</div>
</div>
<div class="setting-row">
<div class="setting-label">
<div>结果分享</div>
<span>支持将解读结果导出为图片、长图或链接等形式。</span>
</div>
<div style="font-size:10px; color:rgba(209,213,219,0.9);">
具体形态可在后续方案中细化。
</div>
</div>
</section>
</div>
</section>
<!-- 说明页:塔罗礼仪 & 使用指南 -->
<section class="page" data-page="help">
<div class="page-header">
<div class="page-title-group">
<div class="page-tagline">GUIDE · 使用说明</div>
<div class="page-title">
<span>如何与塔罗好好相处</span>
<div class="page-title-badge">使用规则 · 文案示例</div>
</div>
</div>
</div>
<div class="page-scroll">
<section class="card-shell">
<p class="page-desc">
塔罗是一种帮助你<strong>看见自己</strong>的工具,而不是预言命运。本页面用于放置引导文案、品牌语气和使用须知。
</p>
<ol class="rules-list">
<li>在开始抽牌前,为这次提问设定一个清晰主题,例如「我和某段关系接下来会如何发展?」。</li>
<li>洗牌与抽牌过程中,尽量保持专注,避免频繁切出页面或被打断。</li>
<li>看到结果时,先观察自己的第一反应:身体是放松、紧绷,还是有某种画面跳出来?这些都是讯息的一部分。</li>
<li>如果对某次解读感到困惑,可以在「记录」中回看几天前的抽牌,有时答案会在时间中慢慢对齐。</li>
<li>本原型仅展示 UI / UX 结构,后续可接入账号体系、付费解读、AI 文案生成等能力。</li>
</ol>
</section>
</div>
</section>
</main>
<nav class="bottom-nav">
<button class="nav-item nav-item--active" data-page-jump="home">
<div class="nav-item-icon">◎</div>
<div>首页</div>
</button>
<button class="nav-item" data-page-jump="spread">
<div class="nav-item-icon">♧</div>
<div>抽牌</div>
</button>
<button class="nav-item" data-page-jump="result">
<div class="nav-item-icon">✶</div>
<div>结果</div>
</button>
<button class="nav-item" data-page-jump="history">
<div class="nav-item-icon">☷</div>
<div>记录</div>
</button>
<button class="nav-item" data-page-jump="help">
<div class="nav-item-icon">?</div>
<div>说明</div>
</button>
</nav>
<div class="app-toast" id="app-toast">
<span>✶</span>
<span>本文件为 UI 原型示例,无真实占卜逻辑。</span>
</div>
</div>
</div>
</div>
<script>
(function () {
const pages = document.querySelectorAll(".page");
const navItems = document.querySelectorAll(".nav-item");
const pageJumpButtons = document.querySelectorAll("[data-page-jump]");
const toast = document.getElementById("app-toast");
const deckArea = document.querySelector(".deck-area");
const saveButtons = document.querySelectorAll(".result-actions .ghost-btn:first-child");
const redoButtons = document.querySelectorAll(".result-actions .ghost-btn:last-child");
const toggles = document.querySelectorAll(".toggle");
let currentPage = "home";
let isDrawing = false;
function showPage(target) {
if (!target || target === currentPage) return;
currentPage = target;
pages.forEach((page) => {
const name = page.getAttribute("data-page");
page.classList.toggle("page--active", name === target);
});
navItems.forEach((item) => {
const jump = item.getAttribute("data-page-jump");
item.classList.toggle("nav-item--active", jump === target);
});
}
pageJumpButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const target = btn.getAttribute("data-page-jump");
showPage(target);
});
});
// 抽牌区域点击 → 跳到结果页并提示
if (deckArea) {
// 触摸按下时表现为洗牌,松手后恢复
deckArea.addEventListener("touchstart", () => {
deckArea.classList.add("deck-area--shuffling");
});
["touchend", "touchcancel"].forEach((evt) => {
deckArea.addEventListener(evt, () => {
deckArea.classList.remove("deck-area--shuffling");
});
});
// 点击触发抽出动画,然后再进入结果页
deckArea.addEventListener("click", () => {
if (isDrawing) return;
isDrawing = true;
deckArea.classList.remove("deck-area--shuffling");
deckArea.classList.add("deck-area--drawing");
setTimeout(() => {
deckArea.classList.remove("deck-area--drawing");
isDrawing = false;
showPage("result");
if (toast) {
toast.querySelector("span:nth-child(2)").textContent = "示例:已为你生成一次占卜结果。";
toast.classList.add("app-toast--visible");
setTimeout(() => toast.classList.remove("app-toast--visible"), 1800);
}
}, 750);
});
}
// 结果页按钮:保存记录 / 重新抽一轮
saveButtons.forEach((btn) => {
btn.addEventListener("click", () => {
if (!toast) return;
toast.querySelector("span:nth-child(2)").textContent = "示例:已添加到「记录」列表(静态 UI)。";
toast.classList.add("app-toast--visible");
setTimeout(() => toast.classList.remove("app-toast--visible"), 1800);
});
});
redoButtons.forEach((btn) => {
btn.addEventListener("click", () => {
showPage("spread");
if (!toast) return;
toast.querySelector("span:nth-child(2)").textContent = "你可以带着新问题再抽一次(原型演示)。";
toast.classList.add("app-toast--visible");
setTimeout(() => toast.classList.remove("app-toast--visible"), 1800);
});
});
// 设置里的开关加一点视觉反馈
toggles.forEach((toggle) => {
toggle.addEventListener("click", () => {
toggle.classList.toggle("toggle--on");
});
});
// 初始轻提示,用于告知这是 UI 原型
setTimeout(() => {
if (!toast) return;
toast.classList.add("app-toast--visible");
setTimeout(() => toast.classList.remove("app-toast--visible"), 2200);
}, 800);
})();
</script>
</body>
</html>
本文作者:lixf6
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!