feat(web): 对齐 Alright 主题视觉规范(配色/字体/图标/背景/动效),并补充设计规范文档
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { ArrowUpIcon } from './Icon'
|
||||
|
||||
/**
|
||||
* 回到顶部:复刻 Alright 主题的 .scroll-toolbar .totop-button。
|
||||
*
|
||||
* - 42px 圆形、浅灰底,固定在右侧(bottom: 50%)
|
||||
* - 默认隐藏,只有内容区滚动后才淡入,回到顶部后再淡出
|
||||
*/
|
||||
type BackToTopProps = {
|
||||
/** 内容区是否已滚动(由 App 传入) */
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
export function BackToTop({ visible }: BackToTopProps) {
|
||||
const handleClick = () => {
|
||||
// 桌面端滚动的是内容容器,窄屏滚动的是窗口,两处都滚一遍
|
||||
document.getElementById('content-scroll')?.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`back-to-top${visible ? ' is-visible' : ''}`}
|
||||
title="回到顶部"
|
||||
aria-label="回到顶部"
|
||||
tabIndex={visible ? 0 : -1}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<ArrowUpIcon />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 数量角标:贴在分区标题右上角的小红标。
|
||||
* 纯展示组件,只接收数字,宽度由内容撑开(两位数不会变形)。
|
||||
*/
|
||||
type CountBadgeProps = {
|
||||
count: number
|
||||
}
|
||||
|
||||
export function CountBadge({ count }: CountBadgeProps) {
|
||||
return <span className="count-badge">{count}</span>
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 通用图标组件。
|
||||
*
|
||||
* 全部使用 Alright 主题的 iconfont 线性图标(见 src/iconfont.css),
|
||||
* 尺寸与颜色由使用处的 CSS 控制(font-size / color),
|
||||
* 因此同一套图标可以复用在按钮、统计条、导航等不同位置。
|
||||
*/
|
||||
|
||||
type IconProps = {
|
||||
/** 附加类名,便于局部覆盖字号或颜色 */
|
||||
className?: string
|
||||
}
|
||||
|
||||
const base = (name: string, className?: string) =>
|
||||
`iconfont icon-${name}${className !== undefined ? ` ${className}` : ''}`
|
||||
|
||||
export function SearchIcon({ className }: IconProps) {
|
||||
return <i className={base('search', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function UserIcon({ className }: IconProps) {
|
||||
return <i className={base('user', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function RefreshIcon({ className }: IconProps) {
|
||||
return <i className={base('refresh', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function LinkIcon({ className }: IconProps) {
|
||||
return <i className={base('link', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ExternalIcon({ className }: IconProps) {
|
||||
return <i className={base('external', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function EyeIcon({ className }: IconProps) {
|
||||
return <i className={base('eye', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ThumbUpIcon({ className }: IconProps) {
|
||||
return <i className={base('thumb-up', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ShareIcon({ className }: IconProps) {
|
||||
return <i className={base('share', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function CloseIcon({ className }: IconProps) {
|
||||
return <i className={base('close', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function MoreIcon({ className }: IconProps) {
|
||||
return <i className={base('more', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ArrowUpIcon({ className }: IconProps) {
|
||||
return <i className={base('arrow-up', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ArrowLeftIcon({ className }: IconProps) {
|
||||
return <i className={base('arrow-left', className)} aria-hidden="true" />
|
||||
}
|
||||
|
||||
export function ArrowDownIcon({ className }: IconProps) {
|
||||
return <i className={base('arrow-down', className)} aria-hidden="true" />
|
||||
}
|
||||
Reference in New Issue
Block a user