feat: complete newspaperui component library with docs site

- 18 React components (Layout/Section/Article/Layer/Masthead/Rule +
  Headline/Subhead/Kicker/BodyText/Quote/Byline/Dateline/Caption +
  Image/Figure/Video/PullQuote)
- Theme: warm off-white palette, Source Serif 4 / Cormorant Garamond /
  Inter / Noto Serif SC/JP, visual weight mapping, dark mode
- Docs: Landing page, 6 Blocks (zh/en/jp), component API docs
- GitHub Pages deployment via static export

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
sunzhongyi
2026-05-20 14:22:14 +08:00
parent 610805a374
commit 1f09bba3ef
57 changed files with 2662 additions and 1127 deletions
@@ -11,6 +11,19 @@ export interface ArticleProps {
children: ReactNode;
}
/**
* Article — 文章块,grid-column span 跨栏
*
* - 通过 span 属性控制在 Section 栅格中占据的列数
* - 自动 clamp 到父 Section 的最大列数
* - 支持 print 分页控制(breakable
*
* @example
* <Article span={14} breakable={false}>
* <Headline weight="High">Breaking News</Headline>
* <BodyText columns={2}>...</BodyText>
* </Article>
*/
export const Article: React.FC<ArticleProps> = ({
span, breakable = true, className, style, children,
}) => {
+12
View File
@@ -13,6 +13,18 @@ export interface LayerProps {
children: ReactNode;
}
/**
* Layer — 浮动层(absolute/fixed/sticky
*
* - 支持 absolute、fixed、sticky 三种定位模式
* - 可精确控制 top/left/right/bottom/zIndex
* - 用于叠加装饰元素、浮动导航或 sticky 侧栏
*
* @example
* <Layer position="sticky" top={0} zIndex={100}>
* <nav>Sticky Navigation</nav>
* </Layer>
*/
export const Layer: React.FC<LayerProps> = ({
position = 'absolute', top, left, right, bottom, zIndex,
className, style, children,
+12
View File
@@ -15,6 +15,18 @@ interface LayoutContextValue { columns: number; }
const LayoutContext = createContext<LayoutContextValue>({ columns: 24 });
export const useLayout = () => useContext(LayoutContext);
/**
* Layout — 顶层容器,提供 24 列栅格 Context
*
* - 通过 LayoutContext 向子组件广播 columns 数量
* - 支持 light/dark 主题切换(data-theme 属性)
* - 居中布局,可配置 maxWidth 和 padding
*
* @example
* <Layout columns={24} theme="light" maxWidth="1280px">
* <Section columns={24}>...</Section>
* </Layout>
*/
export const Layout: React.FC<LayoutProps> = ({
columns = 24, maxWidth = '1280px', padding = 'var(--nui-space-6)',
theme, className, style, children,
@@ -12,6 +12,21 @@ export interface MastheadProps {
className?: string;
}
/**
* Masthead — 报头(classic/blackletter/modern 三种 variant
*
* - classic: Cormorant Garamond 居中衬线风格
* - blackletter: UnifrakturMaguntia 德式哥特体
* - modern: 左对齐现代大标题风格
*
* @example
* <Masthead
* variant="classic"
* title="The Daily Chronicle"
* date="May 19, 2026"
* edition="Vol. CLII No. 42"
* />
*/
export const Masthead: React.FC<MastheadProps> = ({
title, kicker, edition, date, price, variant = 'classic', className,
}) => {
+10
View File
@@ -10,6 +10,16 @@ export interface RuleProps {
style?: CSSProperties;
}
/**
* Rule — 分隔线(hairline/double/thick
*
* - 支持水平和垂直方向
* - hairline: 1px 细线;double: 双线装饰;thick: 3px 粗线
* - 水平模式下可通过 span 控制跨栏宽度
*
* @example
* <Rule variant="double" orientation="horizontal" />
*/
export const Rule: React.FC<RuleProps> = ({
variant = 'hairline', orientation = 'horizontal', span, className, style,
}) => {
@@ -17,6 +17,19 @@ interface SectionContextValue { columns: number; }
const SectionContext = createContext<SectionContextValue>({ columns: 24 });
export const useSection = () => useContext(SectionContext);
/**
* Section — CSS Grid 栅格区域
*
* - 内部创建独立的 grid 容器,列数受父 Layout 约束
* - 支持 hairline divider 分隔线(top/bottom/both
* - 通过 SectionContext 向子 Article 广播当前列数
*
* @example
* <Section columns={24} gap="var(--nui-gutter)" divider="bottom">
* <Article span={14}>...</Article>
* <Article span={10}>...</Article>
* </Section>
*/
export const Section: React.FC<SectionProps> = ({
columns, gap = 'var(--nui-gutter)', breakable = true, divider = 'none',
className, style, children,
+16
View File
@@ -14,6 +14,22 @@ export interface FigureProps {
style?: CSSProperties;
}
/**
* Figure — 图文组合(img + Caption
*
* - 将图片与 Caption 组合为语义化 figure 元素
* - 支持 caption 文字说明和 credit 来源标注
* - 自动避免 print 分页断开(break-inside: avoid
*
* @example
* <Figure
* src="/photo.jpg"
* alt="City skyline"
* caption="Downtown at dusk"
* credit="Photo by John"
* span={10}
* />
*/
export const Figure: React.FC<FigureProps> = ({
src, alt, caption, credit, span, className, style,
}) => {
+10
View File
@@ -11,6 +11,16 @@ export interface ImageProps {
style?: CSSProperties;
}
/**
* Image — 图片
*
* - 响应式全宽图片,自动适配栅格列宽
* - 通过 span 控制在 Section 中占据的列数
* - 必须提供 alt 属性以确保无障碍访问
*
* @example
* <Image src="/photo.jpg" alt="City skyline" span={12} />
*/
export const Image: React.FC<ImageProps> = ({ src, alt, span, className, style }) => {
const section = useSection();
const cols = span ? clampSpan(span, section.columns) : undefined;
@@ -15,6 +15,18 @@ export interface PullQuoteProps {
children: ReactNode;
}
/**
* PullQuote — 拉引(上下双 hairline + Display 字体)
*
* - 上下 hairline 边框包裹,Display 字体突出引文
* - 支持 spanAllColumns 跨越多栏 BodyText 的所有列
* - 可附带 author 署名(small-caps 样式)
*
* @example
* <PullQuote weight="High" author="Jane Doe" align="center">
* "Design is not just what it looks like — design is how it works."
* </PullQuote>
*/
export const PullQuote: React.FC<PullQuoteProps> = ({
weight = 'High', span, spanAllColumns = false, author, align = 'left',
className, style, children,
+16
View File
@@ -15,6 +15,22 @@ export interface VideoProps {
style?: CSSProperties;
}
/**
* Video — 视频
*
* - 响应式视频播放器,支持 poster 封面图
* - 可附带 caption 说明和 credit 来源
* - 自动避免 print 分页断开
*
* @example
* <Video
* src="/clip.mp4"
* poster="/poster.jpg"
* caption="Interview footage"
* credit="Video by Reuters"
* span={14}
* />
*/
export const Video: React.FC<VideoProps> = ({
src, poster, caption, credit, span, controls = true, className, style,
}) => {
+17 -4
View File
@@ -8,17 +8,30 @@ export interface BodyTextProps {
weight?: 'High' | 'Medium' | 'Low';
span?: number;
columns?: 1 | 2 | 3 | 4; // 多栏文字流
columnWidth?: string; // 默认 '18em'
columnFill?: 'auto' | 'balance';
columnWidth?: string; // 可选:显式指定每栏目标宽度(与 columnCount 配合)
columnFill?: 'auto' | 'balance'; // 默认 balance(平均分布到各栏)
dropCap?: boolean;
className?: string;
style?: CSSProperties;
children: ReactNode;
}
/**
* BodyText — 正文(核心:columns 多栏文字流 + dropCap 首字下沉)
*
* - CSS Multi-column 实现真实报纸多栏排版(1-4 栏)
* - dropCap 启用 ::first-letter 首字下沉效果
* - 自动应用 old-style figures、hanging punctuation、column-rule hairline
* - columnFill 默认 'balance',浏览器自动均分内容到各栏(无需指定 height)
*
* @example
* <BodyText columns={3} dropCap>
* <p>The story begins with a dramatic opening paragraph...</p>
* </BodyText>
*/
export const BodyText: React.FC<BodyTextProps> = ({
weight = 'Medium', span, columns = 1, columnWidth = '18em',
columnFill = 'auto', dropCap = false,
weight = 'Medium', span, columns = 1, columnWidth,
columnFill = 'balance', dropCap = false,
className, style, children,
}) => {
const section = useSection();
+10
View File
@@ -9,6 +9,16 @@ export interface BylineProps {
children: ReactNode; // e.g. "BY ALICE SMITH"
}
/**
* Byline — 署名(Inter small-caps
*
* - Inter 字体 + OpenType small-caps 特性
* - 用于文章作者署名,通常置于标题下方
* - 字号、字重、间距由 visualWeights 数据驱动
*
* @example
* <Byline>BY ALICE SMITH</Byline>
*/
export const Byline: React.FC<BylineProps> = ({ className, style, children }) => {
const config = visualWeights.Byline.Standard!;
return (
+12
View File
@@ -10,6 +10,18 @@ export interface CaptionProps {
children: ReactNode;
}
/**
* Caption — 图片说明(italic + credit small-caps
*
* - 主体文字为 italic 衬线,描述图片内容
* - credit 部分以 small-caps 显示摄影师/来源信息
* - 渲染为 figcaption,语义化配合 Figure 使用
*
* @example
* <Caption credit="Photograph by Jane Doe">
* A view of the city skyline at sunset.
* </Caption>
*/
export const Caption: React.FC<CaptionProps> = ({ credit, className, style, children }) => {
const config = visualWeights.Caption.Standard!;
return (
+12
View File
@@ -9,6 +9,18 @@ export interface DatelineProps {
children: ReactNode; // e.g. "LONDON —"
}
/**
* Dateline — 发稿地(Inter small-caps
*
* - Inter 字体 + OpenType small-caps,标注新闻发稿地
* - 通常置于正文首段开头,如 "LONDON —"
* - 渲染为 inline span,可嵌入 BodyText 内
*
* @example
* <BodyText>
* <Dateline>WASHINGTON —</Dateline> The president announced...
* </BodyText>
*/
export const Dateline: React.FC<DatelineProps> = ({ className, style, children }) => {
const config = visualWeights.Dateline.Standard!;
return (
+12
View File
@@ -18,6 +18,18 @@ export interface HeadlineProps {
children: ReactNode;
}
/**
* Headline — 标题(High/Medium/Low 三档,自动映射 h1/h2/h3
*
* - 根据 weight 自动选择语义标签(h1/h2/h3),也可通过 as 覆盖
* - 字号、行高、字重由 visualWeights 数据驱动
* - 支持 text-wrap: balance 自动平衡换行
*
* @example
* <Headline weight="High" span={14}>
* Breaking: Major Event Unfolds
* </Headline>
*/
export const Headline: React.FC<HeadlineProps> = ({
weight = 'High', span, as, align, className, style, children,
}) => {
+11
View File
@@ -9,6 +9,17 @@ export interface KickerProps {
children: ReactNode;
}
/**
* Kicker — 小帽体标签(朱红 accent)
*
* - Inter small-caps 字体,朱红色 accent 色
* - 用于标题上方的栏目标签或分类标识
* - 字号和间距由 visualWeights 数据驱动
*
* @example
* <Kicker>EXCLUSIVE</Kicker>
* <Headline weight="High">Major Discovery</Headline>
*/
export const Kicker: React.FC<KickerProps> = ({ className, style, children }) => {
const config = visualWeights.Kicker.Standard!;
return (
+12
View File
@@ -14,6 +14,18 @@ export interface QuoteProps {
children: ReactNode;
}
/**
* Quote — 引用(block 左缩进 / inline italic
*
* - block 模式:渲染为 blockquote,支持 cite 属性和 span 跨栏
* - inline 模式:渲染为 em 标签,适合行内引用
* - 字号和样式由 visualWeights 数据驱动
*
* @example
* <Quote variant="block" weight="High" cite="https://source.com">
* "The truth is rarely pure and never simple."
* </Quote>
*/
export const Quote: React.FC<QuoteProps> = ({
variant = 'block', weight = 'Medium', span, cite, className, style, children,
}) => {
+12
View File
@@ -13,6 +13,18 @@ export interface SubheadProps {
children: ReactNode;
}
/**
* Subhead — 副标题(italic 衬线)
*
* - 默认 italic 衬线风格,视觉层级低于 Headline
* - 支持 High/Medium 两档 weight
* - 可通过 as 指定渲染标签(h2/h3/h4/p
*
* @example
* <Subhead weight="Medium" span={14}>
* A deeper look into the story behind the headlines
* </Subhead>
*/
export const Subhead: React.FC<SubheadProps> = ({
weight = 'Medium', span, as = 'p', className, style, children,
}) => {