feat: 设计系统四册迁入教程站(design-colors/symbols/components/examples 四页,Page 适配站头 chrome)——navbar 增「设计·」链接组与选中态;组件页卡直链章节页,内容与教程数据同源
This commit is contained in:
1 parent
85673aa80a
commit
810c788a95
15 files changed
+488
-3
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>设计系统 · 颜色 · 鲜艳与碰撞</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-paper text-ink">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/pages/design-colors/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>设计系统 · 组件 · 入口载体与状态</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-paper text-ink">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/pages/design-components/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>设计系统 · 样例 · 组合场景</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-paper text-ink">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/pages/design-examples/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>设计系统 · 符号、形状 · 装饰即语义</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-paper text-ink">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/pages/design-symbols/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/** 散落形状摆放——「卡片附近空当 + 随机」:
|
||||||
|
* 网格采样容器空白 → 过滤出距内容盒([data-comp])≤ near 的空当(不胡乱撒)→
|
||||||
|
* 种子随机挑位(彼此保距)+空当内随机偏移+随机旋转。
|
||||||
|
* 种子固定 → 每次渲染一致(视觉基线可测);容器重排(ResizeObserver)自动重算。 */
|
||||||
|
import { useLayoutEffect, useRef, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
|
function mulberry32(seed: number) {
|
||||||
|
let t = seed >>> 0
|
||||||
|
return () => {
|
||||||
|
t = (t + 0x6d2b79f5) >>> 0
|
||||||
|
let x = Math.imul(t ^ (t >>> 15), 1 | t)
|
||||||
|
x = (x + Math.imul(x ^ (x >>> 7), 61 | x)) ^ x
|
||||||
|
return ((x ^ (x >>> 14)) >>> 0) / 4294967296
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Place = { x: number; y: number; rot: number }
|
||||||
|
|
||||||
|
export function Scatter({
|
||||||
|
shapes,
|
||||||
|
seed = 7,
|
||||||
|
near = 150, // 空当须距某内容盒 ≤ near——只散在卡片附近
|
||||||
|
margin = 34, // 与内容盒保持的最小间隙(≥ 最大形状半对角)
|
||||||
|
step = 30, // 采样网格密度
|
||||||
|
minGap = 80, // 形状彼此最小间距
|
||||||
|
}: {
|
||||||
|
shapes: ReactNode[]
|
||||||
|
seed?: number
|
||||||
|
near?: number
|
||||||
|
margin?: number
|
||||||
|
step?: number
|
||||||
|
minGap?: number
|
||||||
|
}) {
|
||||||
|
const layerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [places, setPlaces] = useState<Place[]>([])
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const layer = layerRef.current
|
||||||
|
const host = layer?.parentElement
|
||||||
|
if (!layer || !host) return
|
||||||
|
|
||||||
|
const place = () => {
|
||||||
|
const hb = host.getBoundingClientRect()
|
||||||
|
const rects = [...host.querySelectorAll('[data-comp]')].map((el) => el.getBoundingClientRect())
|
||||||
|
if (!rects.length || !hb.width) return
|
||||||
|
const dist = (x: number, y: number, r: DOMRect) =>
|
||||||
|
Math.hypot(Math.max(r.left - x, 0, x - r.right), Math.max(r.top - y, 0, y - r.bottom))
|
||||||
|
const cand: { x: number; y: number }[] = []
|
||||||
|
for (let x = 36; x < hb.width - 36; x += step)
|
||||||
|
for (let y = 36; y < hb.height - 36; y += step) {
|
||||||
|
const px = hb.left + x
|
||||||
|
const py = hb.top + y
|
||||||
|
if (rects.every((r) => dist(px, py, r) >= margin) && rects.some((r) => dist(px, py, r) <= near))
|
||||||
|
cand.push({ x, y })
|
||||||
|
}
|
||||||
|
const rnd = mulberry32(seed)
|
||||||
|
for (let i = cand.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(rnd() * (i + 1))
|
||||||
|
;[cand[i], cand[j]] = [cand[j], cand[i]]
|
||||||
|
}
|
||||||
|
const taken: Place[] = []
|
||||||
|
for (const c of cand) {
|
||||||
|
if (taken.length >= shapes.length) break
|
||||||
|
if (taken.some((t) => Math.hypot(t.x - c.x, t.y - c.y) < minGap)) continue
|
||||||
|
taken.push({ x: c.x + (rnd() - 0.5) * 14, y: c.y + (rnd() - 0.5) * 14, rot: Math.round((rnd() - 0.5) * 52) })
|
||||||
|
}
|
||||||
|
setPlaces(taken)
|
||||||
|
}
|
||||||
|
|
||||||
|
place()
|
||||||
|
const ro = new ResizeObserver(place)
|
||||||
|
ro.observe(host)
|
||||||
|
return () => ro.disconnect()
|
||||||
|
}, [seed, near, margin, step, minGap, shapes.length])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={layerRef} data-scatter-layer aria-hidden className="pointer-events-none absolute inset-0">
|
||||||
|
{places.map((p, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
data-scatter
|
||||||
|
className="absolute"
|
||||||
|
style={{ left: p.x, top: p.y, transform: `translate(-50%, -50%) rotate(${p.rot}deg)` }}
|
||||||
|
>
|
||||||
|
{shapes[i]}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -430,3 +430,15 @@ export const CONTENT: Record<ChapterId, ChapterContent> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 指派学习块示例数据(设计系统·组件/样例页演示用) */
|
||||||
|
export const ASSIGNMENT: import('./types').Assignment = {
|
||||||
|
title: '指派学习 · HTTP 协议',
|
||||||
|
pos: 'Web 通信的地基,旁边是 DNS(找机器)与 TCP(铺线路)',
|
||||||
|
expect: '能向 agent 讲清一次请求的往返:URL → DNS → 连接 → 请求行/头/体 → 响应 → 渲染',
|
||||||
|
scenes: ['看 Network 面板复述一次页面加载', '用 curl 拿一个网页的原始响应'],
|
||||||
|
miss: {
|
||||||
|
how: '装了但没跑',
|
||||||
|
looks: '命令成功、页面空白——缺「真机验收」这块积木',
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,10 +1,20 @@
|
|||||||
/** 教程站页面骨架:站头导航跨页常驻(view-transition-name 共享元素,页间不闪烁)。
|
/** 教程站页面骨架:站头导航跨页常驻(view-transition-name 共享元素,页间不闪烁)。
|
||||||
* 选中项背景色=该章色(与章头 title 横幅统一);书架选中为 ink。 */
|
* 选中项背景色=该章色(与章头 title 横幅统一);书架/设计册选中为 ink。 */
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
import type { ChapterId } from './types'
|
import type { ChapterId } from './types'
|
||||||
import { CHAPTERS } from './chapters'
|
import { CHAPTERS } from './chapters'
|
||||||
import '../styles/tokens.css'
|
import '../styles/tokens.css'
|
||||||
|
|
||||||
|
/** 设计系统四册(自 design-lab 迁入) */
|
||||||
|
export type DesignKey = 'design-colors' | 'design-symbols' | 'design-components' | 'design-examples'
|
||||||
|
|
||||||
|
const DESIGN: { href: string; label: string; key: DesignKey }[] = [
|
||||||
|
{ href: '/design-colors.html', label: '颜色', key: 'design-colors' },
|
||||||
|
{ href: '/design-symbols.html', label: '符号形状', key: 'design-symbols' },
|
||||||
|
{ href: '/design-components.html', label: '组件', key: 'design-components' },
|
||||||
|
{ href: '/design-examples.html', label: '样例', key: 'design-examples' },
|
||||||
|
]
|
||||||
|
|
||||||
const NAV: { href: string; label: string; id?: ChapterId }[] = [
|
const NAV: { href: string; label: string; id?: ChapterId }[] = [
|
||||||
{ href: '/index.html', label: '书架' },
|
{ href: '/index.html', label: '书架' },
|
||||||
...([1, 2, 3, 4, 5, 6] as ChapterId[]).map((id) => ({
|
...([1, 2, 3, 4, 5, 6] as ChapterId[]).map((id) => ({
|
||||||
@@ -23,8 +33,8 @@ export function Page({
|
|||||||
/** 章节页标题在章头横幅里,可省 */
|
/** 章节页标题在章头横幅里,可省 */
|
||||||
title?: string
|
title?: string
|
||||||
desc?: string
|
desc?: string
|
||||||
/** 站头高亮:章节 id 或书架 'index' */
|
/** 站头高亮:章节 id / 书架 / 设计系统册 */
|
||||||
active?: ChapterId | 'index'
|
active?: ChapterId | 'index' | DesignKey
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -51,6 +61,21 @@ export function Page({
|
|||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
<span className="mx-1 h-4 w-[2px] rounded bg-ink/20" aria-hidden />
|
||||||
|
{DESIGN.map((n) => {
|
||||||
|
const on = n.key === active
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={n.href}
|
||||||
|
href={n.href}
|
||||||
|
className={`rounded-lg px-2.5 py-1 text-[12.5px] font-bold no-underline transition-colors ${
|
||||||
|
on ? 'bg-ink text-paper' : 'text-body hover:bg-black/5'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
设计·{n.label}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</header>
|
</header>
|
||||||
{(title || desc) && (
|
{(title || desc) && (
|
||||||
<div className="mb-7">
|
<div className="mb-7">
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { Page, Section } from '../../lib/ui'
|
||||||
|
|
||||||
|
const PAIRS = [
|
||||||
|
{ a: '#FF6B6B', b: '#4ECDC4', name: '对 1 · coral × teal', use: '主用对:课程相框与主图形' },
|
||||||
|
{ a: '#FFD93D', b: '#A78BFA', name: '对 2 · mustard × violet', use: '辅助对:指派与脱靶等警示类' },
|
||||||
|
{ a: '#FF8FAB', b: '#5B8DEF', name: '对 3 · pink × blue', use: '点缀对:代理区与散落形状' },
|
||||||
|
]
|
||||||
|
const NEUTRALS = [
|
||||||
|
{ c: '#111111', n: 'ink', h: '#111111 · 描边/字/硬投影' },
|
||||||
|
{ c: '#FFF9F0', n: 'paper', h: '#FFF9F0 · 卡面/大底' },
|
||||||
|
]
|
||||||
|
const CHAPTERS6 = [
|
||||||
|
{ c: '#FF6B6B', n: 'ch1 快速开始' }, { c: '#FF8FAB', n: 'ch2 React' }, { c: '#FFD93D', n: 'ch3 更多示例' },
|
||||||
|
{ c: '#4ECDC4', n: 'ch4 更多' }, { c: '#5B8DEF', n: 'ch5 全栈基础' }, { c: '#A78BFA', n: 'ch6 高级教程' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function Chip({ c, n, h }: { c: string; n: string; h: string }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="h-12 rounded-[10px] border-2 border-ink" style={{ background: c }} />
|
||||||
|
<div className="mt-1 whitespace-nowrap text-[10.5px] font-bold">{n}</div>
|
||||||
|
<div className="whitespace-nowrap font-mono text-[9px] text-[#6b6b6b]">{h}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 三条规则的「为什么」:鲜艳如何成立、如何被收束、注意力如何分配 */
|
||||||
|
const RULES = [
|
||||||
|
{
|
||||||
|
name: '相邻必撞色(跨对取色)',
|
||||||
|
why: '孟菲斯的鲜艳靠碰撞成立:两个高饱和色块相邻时,只有色相拉开距离,视觉才读得出「两个声音」。同色相邻只是一个声音变大——画面发平发闷,鲜艳互相抵消;跨对取色(coral 挨 teal、mustard 挨 violet)反而把彼此衬得更亮。拿到相邻色块时问一句「它俩是不是同一对」,是,就换掉一个。',
|
||||||
|
badCap: '同色相邻失去碰撞',
|
||||||
|
goodCap: '相邻必撞色(跨对取色)',
|
||||||
|
badDemo: <div className="flex overflow-hidden rounded-lg border-2 border-ink"><div className="h-10 w-20 bg-clash1a" /><div className="h-10 w-20 bg-clash1a" /></div>,
|
||||||
|
goodDemo: <div className="flex overflow-hidden rounded-lg border-2 border-ink"><div className="h-10 w-20 bg-clash1a" /><div className="h-10 w-20 bg-clash1b" /></div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '黑描边收束鲜艳',
|
||||||
|
why: '高饱和色放在浅纸上会「振动」:边缘发虚、像悬浮在页面上。2px ink 描边给每块鲜艳一道明确的边界,把颜色关进形状里——这也是拟物卡世界的基础:卡片、形状、站点共用同一粗细的墨线,颜色再艳也是「零件」而不是「光」,缩放与低分屏上轮廓依然清晰。去掉描边,鲜艳就是没裁过的布料。',
|
||||||
|
badCap: '无描边的鲜艳漂浮刺眼',
|
||||||
|
goodCap: '一切鲜艳以 2px ink 描边为界',
|
||||||
|
badDemo: <div className="h-10 w-40 rounded-lg bg-clash2a" />,
|
||||||
|
goodDemo: <div className="h-10 w-40 rounded-lg border-2 border-ink bg-clash2a shadow-hard" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '大底纸色,鲜艳只进局部',
|
||||||
|
why: '鲜艳是注意力,铺满屏幕等于没有注意力。大底永远是 paper,鲜艳只进相框、站点、散落形状这些局部(≤10%/屏),视线才有落点、正文才永远可读;大底一旦鲜艳,字被淹没,卡片也失去「纸上的物」的载体感。记住层级:大底是世界,卡片是物,鲜艳是物上的标记。',
|
||||||
|
badCap: '大底鲜艳,字被淹没',
|
||||||
|
goodCap: '大底纸色,鲜艳只进局部',
|
||||||
|
badDemo: <div className="flex h-10 w-40 items-center justify-center rounded-lg border-2 border-ink bg-clash1b text-[11px] font-bold">大底鲜艳</div>,
|
||||||
|
goodDemo: <div className="flex h-10 w-40 items-center justify-center rounded-lg border-2 border-ink bg-paper text-[11px] font-bold">纸底安放正文</div>,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function DoDont({ bad, children, text }: { bad?: boolean; children: React.ReactNode; text: string }) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-1 items-center gap-3">
|
||||||
|
<span className={`flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 border-ink text-[11px] font-extrabold ${bad ? 'bg-clash1a text-paper' : 'bg-clash1b'}`}>
|
||||||
|
{bad ? '✗' : '✓'}
|
||||||
|
</span>
|
||||||
|
{children}
|
||||||
|
<p className="m-0 flex-1 text-[11.5px] leading-snug text-body">{text}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Page active="design-colors" title="颜色 · 鲜艳与碰撞" desc="三组撞色对轮转,黑描边收束一切鲜艳,大底永远是纸色。章节色只进相框,状态不用颜色——用形状。">
|
||||||
|
<Section label="撞色对" note="相邻的鲜艳必须来自不同对">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{PAIRS.map((p) => (
|
||||||
|
<div key={p.name}>
|
||||||
|
<div className="flex overflow-hidden rounded-xl border-2 border-ink">
|
||||||
|
<div className="h-11 w-[88px]" style={{ background: p.a }} />
|
||||||
|
<div className="h-11 w-[88px]" style={{ background: p.b }} />
|
||||||
|
</div>
|
||||||
|
<div className="mt-1.5 text-[11px] font-bold">{p.name}</div>
|
||||||
|
<div className="text-[10px] text-[#555]">{p.use}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="中性阶" note="纸的世界——字、边、投影、底">
|
||||||
|
<div className="grid grid-cols-2 gap-3">{NEUTRALS.map((x) => <Chip key={x.n} {...x} />)}</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="章节色" note="六章六色,只进 16:9 相框与图标瓦">
|
||||||
|
<div className="grid grid-cols-6 gap-3">{CHAPTERS6.map((x) => <Chip key={x.n} {...x} h={x.c} />)}</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="规则与反例" note="先讲为什么这样设计,再看 ✗/✓ 对照">
|
||||||
|
<div className="space-y-5">
|
||||||
|
{RULES.map((r, i) => (
|
||||||
|
<div key={r.name}>
|
||||||
|
<div className="flex items-baseline gap-2">
|
||||||
|
<span className="flex h-[18px] w-[18px] shrink-0 translate-y-[2px] items-center justify-center rounded-full border-2 border-ink bg-ink text-[10px] font-extrabold text-paper">{i + 1}</span>
|
||||||
|
<b className="text-[13px]">{r.name}</b>
|
||||||
|
</div>
|
||||||
|
<p className="m-0 mt-1.5 mb-2 max-w-[760px] text-[11.5px] leading-relaxed text-body">{r.why}</p>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<DoDont bad text={r.badCap}>{r.badDemo}</DoDont>
|
||||||
|
<DoDont text={r.goodCap}>{r.goodDemo}</DoDont>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import { App } from './App'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<App />)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Page, Section } from '../../lib/ui'
|
||||||
|
import { ASSIGNMENT, CHAPTERS } from '../../lib/chapters'
|
||||||
|
import { MemphisCard } from '../../components/MemphisCard'
|
||||||
|
import { AssignBlock, Ticket } from '../../components/Ticket'
|
||||||
|
import { EntryGuide } from '../../components/EntryGuide'
|
||||||
|
import { Mark } from '../../components/shapes'
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Page active="design-components" title="组件 · 入口载体与状态" desc="CourseCard(入口)· 票券(状态)· 指派块+脱靶标记(任务与警示)· 入口画像。线条的形式规格见 02 符号形状,与卡片的实际组合见 04 样例。">
|
||||||
|
<Section label="CourseCard" note="卡头三件套=靶子;hover 拿起播放 GIF,8000ms 翻转;末张=价值区标记用法">
|
||||||
|
<div className="flex flex-wrap items-start gap-8">
|
||||||
|
<div className="w-[280px]"><MemphisCard chapter={CHAPTERS[1]} entry={1} /></div>
|
||||||
|
<div className="w-[280px]"><MemphisCard chapter={CHAPTERS[2]} entry={2} /></div>
|
||||||
|
<div className="w-[160px]"><MemphisCard chapter={CHAPTERS[5]} compact /></div>
|
||||||
|
<div className="w-[280px]">
|
||||||
|
<MemphisCard
|
||||||
|
chapter={CHAPTERS[4]}
|
||||||
|
zoneText={<>这一步过程就地写清:<Mark>环境与依赖如何咬合</Mark></>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="票券 & 指派学习块" note="票=完成状态;块=位置/预期/场景+脱靶标记">
|
||||||
|
<div className="flex flex-wrap items-start gap-8">
|
||||||
|
<Ticket chapter={CHAPTERS[1]} done />
|
||||||
|
<Ticket chapter={CHAPTERS[5]} done={false} />
|
||||||
|
<AssignBlock {...ASSIGNMENT} />
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="入口梳理" note="推荐不是强制——无锁">
|
||||||
|
<EntryGuide />
|
||||||
|
</Section>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import { App } from './App'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<App />)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/** 组合场景=大部分组件拿出来的真实使用,不做解释性标注:
|
||||||
|
* 入口画像 → 课程卡与关系线 → 进度票券 → 指派块;形状散落于卡片附近空当。 */
|
||||||
|
import { Page } from '../../lib/ui'
|
||||||
|
import { ASSIGNMENT, CHAPTERS, EDGES } from '../../lib/chapters'
|
||||||
|
import { MemphisCard } from '../../components/MemphisCard'
|
||||||
|
import { RouteLine } from '../../components/RouteLine'
|
||||||
|
import { AssignBlock, Ticket } from '../../components/Ticket'
|
||||||
|
import { EntryGuide } from '../../components/EntryGuide'
|
||||||
|
import { Scatter } from '../../components/Scatter'
|
||||||
|
import { Cross, Ring, Tremble, Tri, Wave } from '../../components/shapes'
|
||||||
|
|
||||||
|
/* 场景坐标(节点=卡描边真实边缘):卡1(40,110,250) 卡2(380,44,250,rot) 卡3(700,44,140) */
|
||||||
|
const nodes = {
|
||||||
|
1: { id: 1, borderLeft: 40, right: 290, y: 180 },
|
||||||
|
2: { id: 2, borderLeft: 380, right: 630, y: 108 },
|
||||||
|
5: { id: 5, borderLeft: 700, right: 840, y: 108 },
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Page active="design-examples" title="样例 · 组合场景" desc="大部分组件同台真实使用:入口画像 → 课程卡与关系线 → 进度票券 → 指派块;形状散落于卡片附近空当。">
|
||||||
|
<EntryGuide />
|
||||||
|
<div className="relative mt-8 h-[600px] overflow-hidden rounded-2xl bg-paper">
|
||||||
|
<Scatter
|
||||||
|
shapes={[
|
||||||
|
<Tri size={26} key="tri" />,
|
||||||
|
<Ring size={22} key="ring" />,
|
||||||
|
<Cross size={20} key="cross1" />,
|
||||||
|
<Cross size={18} key="cross2" />,
|
||||||
|
<Wave size={44} key="wave" />,
|
||||||
|
<Tremble size={20} key="tremble" />,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<RouteLine edges={EDGES} nodes={nodes} width={1160} height={600} />
|
||||||
|
<div className="absolute left-[40px] top-[110px] w-[250px]"><MemphisCard chapter={CHAPTERS[1]} entry={1} /></div>
|
||||||
|
<div className="absolute left-[380px] top-[44px] w-[250px] -rotate-1"><MemphisCard chapter={CHAPTERS[2]} entry={2} /></div>
|
||||||
|
<div className="absolute left-[700px] top-[44px] w-[140px]"><MemphisCard chapter={CHAPTERS[5]} compact /></div>
|
||||||
|
<div className="absolute left-[860px] top-[320px]"><AssignBlock {...ASSIGNMENT} /></div>
|
||||||
|
<div className="absolute left-[60px] top-[440px] flex gap-4">
|
||||||
|
<Ticket chapter={CHAPTERS[2]} done />
|
||||||
|
<Ticket chapter={CHAPTERS[5]} done={false} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import { App } from './App'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<App />)
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { Page, Section } from '../../lib/ui'
|
||||||
|
import { BrickDot, Cross, Mark, Ring, Tremble, Tri, Wave } from '../../components/shapes'
|
||||||
|
import { CHAPTERS } from '../../lib/chapters'
|
||||||
|
import { Ticket } from '../../components/Ticket'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
|
const SHAPES = [
|
||||||
|
{ el: <Tri size={28} />, n: '▲ 靶子', m: '目标,卡头打头', s: '靶子先行' },
|
||||||
|
{ el: <span className="flex"><BrickDot on /><BrickDot /></span>, n: '● 积木', m: '清单点列,实=已学', s: '积木清单' },
|
||||||
|
{ el: <Ring size={28} />, n: '○ 预期', m: '可核对的结果', s: '预期结果' },
|
||||||
|
{ el: <Wave size={36} />, n: '~ 代理区', m: '交给 agent', s: '每步先分类' },
|
||||||
|
{ el: <Mark>过程</Mark>, n: '荧光标记 价值区', m: '划在文字上,不是散落图形', s: '每步先分类' },
|
||||||
|
{ el: <Tremble size={22} />, n: '⌇ 脱靶', m: '图形入口:追随图形进入阅读', s: '脱靶提醒' },
|
||||||
|
{ el: <Cross size={22} />, n: '+ 指派', m: '位置/预期/场景', s: '指派学习' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const VOCAB: { p: string; rows: [ReactNode, string][] }[] = [
|
||||||
|
{ p: '靶子', rows: [['▲ 目标', '做什么'], ['○ 预期结果', '做成长什么样、怎么核对'], ['● 积木清单', '要用到的零件——只列用到的;说零件不说知识']] },
|
||||||
|
{ p: '步骤分类', rows: [['~ 代理区', '无学习价值——交给 agent'], [<Mark key="v">价值区</Mark>, '有学习价值——就地写清']] },
|
||||||
|
{ p: '指派学习', rows: [['+ 三样(图形入口)', '教材只留三样,讲解交给 agent'], ['└ 位置', '知识体系中的坐标'], ['└ 预期', '实践结果应长什么样——当尺子'], ['└ 场景', '带回 agent 再做的练习——管巩固']] },
|
||||||
|
{ p: '脱靶提醒', rows: [['⌇ 图形标记', '不人为挖坑;跟在指派后'], ['└ 可能性 / 长相', '怎么脱靶+脱靶什么样——撞上认出缺哪块']] },
|
||||||
|
{ p: '认知基础', rows: [['底座纹·真机验收章', 'env/库/运行环境——脚下的地,不是积木']] },
|
||||||
|
]
|
||||||
|
|
||||||
|
const LINES = [
|
||||||
|
{ d: 'M 8 34 H 60 A 8 8 0 0 0 68 26 V 18 A 8 8 0 0 1 76 10 H 180', w: 2.5, dash: '', cap: 'butt' as const, st: true, n: '实线 = 先后', m: '推荐顺序 · 主线,唯一带站点' },
|
||||||
|
{ d: 'M 8 23 H 180', w: 2, dash: '4 2', cap: 'butt' as const, st: false, n: '虚线 = 相关', m: '内容互参 · 无顺序,无站点' },
|
||||||
|
{ d: 'M 8 23 H 180', w: 2.4, dash: '0.1 6', cap: 'round' as const, st: false, n: '点线 = 同族', m: '同一积木家族(教一块带一族)' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Page active="design-symbols" title="符号、形状 · 装饰即语义" desc="形状对内容下定义、散落于空白;线条形式承载关系含义;票券只表达完成状态。">
|
||||||
|
<Section label="语义形状 ×7" note="散落=卡片附近空当+随机旋转/落点(Scatter 种子化),密度 ≤ 8/屏">
|
||||||
|
<div className="grid grid-cols-4 gap-3.5 md:grid-cols-7">
|
||||||
|
{SHAPES.map((s) => (
|
||||||
|
<div key={s.n} className="text-center">
|
||||||
|
<div className="mx-auto flex h-[46px] w-[46px] items-center justify-center rounded-xl border-2 border-ink bg-white">{s.el}</div>
|
||||||
|
<div className="mt-1.5 text-[11px] font-bold">{s.n}</div>
|
||||||
|
<div className="text-[9.5px] leading-tight text-[#6b6b6b]">{s.m}</div>
|
||||||
|
<div className="mt-0.5 text-[9px] text-[#8a8a8a]">{s.s}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="词汇表 · 父子层级" note="与 ai-era-tutorial 词汇表对齐">
|
||||||
|
<table className="w-full border-collapse text-[11.5px] text-body">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b-2 border-[#d5d5d3] text-left text-ink">
|
||||||
|
<th className="w-24 py-1 pr-2">父词</th>
|
||||||
|
<th className="w-44 py-1 pr-2">图形 · 子词</th>
|
||||||
|
<th className="py-1 pr-2">释义(skill 口径)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{VOCAB.map((g) =>
|
||||||
|
g.rows.map(([sub, def], i) => (
|
||||||
|
<tr key={g.p + i} className="border-b border-[#ececea]">
|
||||||
|
{i === 0 && <td rowSpan={g.rows.length} className="py-1 pr-2 align-top font-extrabold text-ink">{g.p}</td>}
|
||||||
|
<td className="py-1 pr-2">{sub}</td>
|
||||||
|
<td className="py-1 pr-2">{def}</td>
|
||||||
|
</tr>
|
||||||
|
)),
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Section>
|
||||||
|
<Section label="线条形式 ×4" note="形式即含义;横平竖直+R8 全形式通用">
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{LINES.map((l) => (
|
||||||
|
<div key={l.n} className="rounded-xl border-2 border-ink bg-white p-3">
|
||||||
|
<svg width="188" height="44" viewBox="0 0 188 44">
|
||||||
|
<path d={l.d} stroke="var(--color-ink)" strokeWidth={l.w} fill="none" strokeDasharray={l.dash} strokeLinecap={l.cap || 'butt'} />
|
||||||
|
{l.st && <>
|
||||||
|
<circle cx="8" cy="34" r="5.5" fill="var(--color-clash2a)" stroke="var(--color-ink)" strokeWidth="2" />
|
||||||
|
<circle cx="181" cy="10" r="5.5" fill="var(--color-clash1b)" stroke="var(--color-ink)" strokeWidth="2" />
|
||||||
|
</>}
|
||||||
|
</svg>
|
||||||
|
<div className="mt-1 text-[13px] font-extrabold">{l.n}</div>
|
||||||
|
<div className="text-[11px] text-[#555]">{l.m}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="rounded-xl border-2 border-ink bg-white p-3">
|
||||||
|
<svg width="188" height="44" viewBox="0 0 188 44">
|
||||||
|
<path d="M12 22 q10 -22 20 0 t20 0 t20 0 t20 0 t20 0 t20 0 t20 0 t20 0 t20 0" stroke="var(--color-clash3b)" strokeWidth="3" fill="none" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
<div className="mt-1 text-[13px] font-extrabold">波浪带 = 代理交接</div>
|
||||||
|
<div className="text-[11px] text-[#555]">agent 边界 · 区域标记,不连线</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
<Section label="票券" note="状态的载体——完成即打孔(裁切式实现)">
|
||||||
|
<div className="flex flex-wrap items-start gap-6">
|
||||||
|
<Ticket chapter={CHAPTERS[1]} done />
|
||||||
|
<Ticket chapter={CHAPTERS[5]} done={false} />
|
||||||
|
<p className="m-0 max-w-[280px] flex-1 text-[11.5px] leading-relaxed text-body">
|
||||||
|
<b>完成=沿撕线打双孔</b>(半圆缺口);未开始撕线为实线、无孔。头部色块无独立描边,由票体 overflow-hidden 裁出单一描边圆角。票号=章程序号。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import { App } from './App'
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<App />)
|
||||||
Reference in new issue
Block a user