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:
@@ -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,
|
||||
}) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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,
|
||||
}) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}) => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
}) => {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
}) => {
|
||||
|
||||
@@ -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,
|
||||
}) => {
|
||||
|
||||
Reference in New Issue
Block a user