diff --git a/HANDOFF.md b/HANDOFF.md index 54b8318..8cde420 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -19,6 +19,8 @@ - 调整 `packages/docs/components/Sidebar.tsx` sticky `top: 65px` - **NYT 头版迁移到 `/examples/nyt-frontpage`** - 新建 `packages/docs/app/page.tsx` 作为报纸风格 Landing Page,使用 Masthead + 7 个 demo 卡片垂直分布 +10. **P0 组件扩展**:新增 4 个组件(Folio / IndexBox / Factbox / JumpLine),总计 24 个 +11. **Landing Page 重做**:首页改为完整 demo 报纸风格,直接展示组件能力 ### ⏳ 未完成 @@ -36,7 +38,7 @@ ├── packages/ │ ├── theme/ # CSS variables + 视觉权重表 + 字体 + 排版工具类 │ ├── utils/ # validateSpan / clampSpan / cx -│ ├── components/ # 20 个 React 组件 +│ ├── components/ # 24 个 React 组件 │ └── docs/ # Next.js 15 文档站 ├── design.md # 设计规范(已同步修订版) ├── HANDOFF.md # 本文档 @@ -598,8 +600,8 @@ pnpm --filter @newspaperui/docs dev │ │ ├── cx.ts 类名合并 │ │ └── index.ts │ ├── components/src/ -│ │ ├── layout/ Layout/Section/Article/Layer/Masthead/Rule/RelatedArticles -│ │ ├── text/ Headline/Subhead/Kicker/BodyText/Quote/Byline/Dateline/Caption/AuthorCard +│ │ ├── layout/ Layout/Section/Article/Layer/Masthead/Rule/RelatedArticles/Footer/Sidebar/BreakingNewsBanner/Folio/IndexBox/Factbox +│ │ ├── text/ Headline/Subhead/Kicker/BodyText/Quote/Byline/Dateline/Caption/AuthorCard/JumpLine │ │ ├── media/ Image/Figure/Video/PullQuote │ │ └── index.ts │ └── docs/ diff --git a/README.md b/README.md index 352251f..9f4a39a 100644 --- a/README.md +++ b/README.md @@ -32,16 +32,16 @@ import { Layout, Section, Article, Masthead, BodyText } from '@newspaperui/compo |---------|-------------| | `@newspaperui/theme` | CSS variables, visual weights, typography utilities, Google Fonts | | `@newspaperui/utils` | Grid validation (`validateSpan`, `clampSpan`, `cx`) | -| `@newspaperui/components` | 20 React components | +| `@newspaperui/components` | 24 React components | | `@newspaperui/docs` | Next.js documentation site with live demos | -## Components (20) +## Components (24) ### Layout -`Layout` · `Section` · `Article` · `Layer` · `Masthead` · `Rule` · `RelatedArticles` +`Layout` · `Section` · `Article` · `Layer` · `Masthead` · `Rule` · `Footer` · `Sidebar` · `BreakingNewsBanner` · `Folio` · `IndexBox` · `Factbox` · `RelatedArticles` ### Text -`Headline` · `Subhead` · `Kicker` · `BodyText` · `Quote` · `Byline` · `Dateline` · `Caption` · `AuthorCard` +`Headline` · `Subhead` · `Kicker` · `BodyText` · `Quote` · `Byline` · `Dateline` · `Caption` · `AuthorCard` · `JumpLine` ### Media `Image` · `Figure` · `Video` · `PullQuote` diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index fb5c882..1591468 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -52,6 +52,16 @@ export type { PullQuoteProps } from './media/PullQuote'; export { RelatedArticles } from './layout/RelatedArticles'; export type { RelatedArticlesProps, RelatedArticle } from './layout/RelatedArticles'; +// additional layout +export { Folio } from './layout/Folio'; +export type { FolioProps } from './layout/Folio'; +export { IndexBox } from './layout/IndexBox'; +export type { IndexBoxProps, IndexItem } from './layout/IndexBox'; +export { Factbox } from './layout/Factbox'; +export type { FactboxProps } from './layout/Factbox'; + // additional text export { AuthorCard } from './text/AuthorCard'; export type { AuthorCardProps } from './text/AuthorCard'; +export { JumpLine } from './text/JumpLine'; +export type { JumpLineProps } from './text/JumpLine'; diff --git a/packages/components/src/layout/Factbox.tsx b/packages/components/src/layout/Factbox.tsx new file mode 100644 index 0000000..7ca8b05 --- /dev/null +++ b/packages/components/src/layout/Factbox.tsx @@ -0,0 +1,83 @@ +'use client'; +import React, { ReactNode, CSSProperties } from 'react'; +import { clampSpan, cx } from 'newspaperui-utils'; +import { useSection } from './Section'; + +export interface FactboxProps { + title?: string; + span?: number; + variant?: 'default' | 'highlight' | 'timeline'; + className?: string; + style?: CSSProperties; + children: ReactNode; +} + +/** + * Factbox — 嵌入正文的信息框 + * + * 用于放关键数据、时间线、人物简介等结构化信息。 + * InDesign 中称为 "Anchored Object"。 + * + * @example + * + * + * + */ +export const Factbox: React.FC = ({ + title, span, variant = 'default', className, style, children, +}) => { + const section = useSection(); + const cols = span ? clampSpan(span, section.columns) : undefined; + + const variantStyles: Record = { + default: { + border: '1px solid var(--nui-rule-hairline)', + borderTop: '3px solid var(--nui-rule-decorative)', + background: 'var(--nui-bg-surface)', + }, + highlight: { + border: '1px solid var(--nui-rule-hairline)', + borderLeft: '4px solid var(--nui-accent-primary)', + background: 'var(--nui-bg-surface)', + }, + timeline: { + border: '1px solid var(--nui-rule-hairline)', + borderTop: '3px solid var(--nui-accent-ink-blue)', + background: 'var(--nui-bg-surface)', + }, + }; + + return ( + + ); +}; diff --git a/packages/components/src/layout/Folio.tsx b/packages/components/src/layout/Folio.tsx new file mode 100644 index 0000000..9a2c5bc --- /dev/null +++ b/packages/components/src/layout/Folio.tsx @@ -0,0 +1,50 @@ +'use client'; +import React, { CSSProperties } from 'react'; +import { cx } from 'newspaperui-utils'; + +export interface FolioProps { + page: string; // e.g. "A2" + section?: string; // e.g. "要闻" / "National" + date?: string; // e.g. "2026年5月21日" + publication?: string; // e.g. "人民周报" + align?: 'left' | 'center' | 'between'; + className?: string; + style?: CSSProperties; +} + +/** + * Folio — 版面页眉(页码 + 版名 + 日期) + * + * 报纸每个内页顶部的标识条,告诉读者当前在哪个版面。 + * + * @example + * + */ +export const Folio: React.FC = ({ + page, section, date, publication, align = 'between', className, style, +}) => ( +
+ {page} + {section && {section}} + {publication && {publication}} + {date && {date}} +
+); diff --git a/packages/components/src/layout/IndexBox.tsx b/packages/components/src/layout/IndexBox.tsx new file mode 100644 index 0000000..e4a58a8 --- /dev/null +++ b/packages/components/src/layout/IndexBox.tsx @@ -0,0 +1,92 @@ +'use client'; +import React, { CSSProperties } from 'react'; +import { cx } from 'newspaperui-utils'; + +export interface IndexItem { + page: string; // "A2" + title: string; // "经济" / "National" + headline?: string; // optional headline preview +} + +export interface IndexBoxProps { + title?: string; + items: IndexItem[]; + className?: string; + style?: CSSProperties; +} + +/** + * IndexBox — 头版内容索引框 + * + * 告诉读者今天各版有什么内容。通常放在头版右上角或底部。 + * + * @example + * + */ +export const IndexBox: React.FC = ({ + title = 'Inside', items, className, style, +}) => ( +
+

{title}

+
    + {items.map((item, i) => ( +
  • + {item.page} + {item.title} + {item.headline && ( + {item.headline} + )} +
  • + ))} +
+
+); diff --git a/packages/components/src/text/JumpLine.tsx b/packages/components/src/text/JumpLine.tsx new file mode 100644 index 0000000..8284027 --- /dev/null +++ b/packages/components/src/text/JumpLine.tsx @@ -0,0 +1,41 @@ +'use client'; +import React, { CSSProperties } from 'react'; +import { cx } from 'newspaperui-utils'; + +export interface JumpLineProps { + direction: 'to' | 'from'; // 'to' = "续转第X版", 'from' = "接第X版" + page: string; // e.g. "A6" + className?: string; + style?: CSSProperties; +} + +/** + * JumpLine — 跨版续转标注 + * + * 报纸文章从一个版面跳转到另一个版面时的标注。 + * + * @example + * + * + */ +export const JumpLine: React.FC = ({ + direction, page, className, style, +}) => ( +
+ {direction === 'to' ? `Continued on ${page} →` : `← Continued from ${page}`} +
+); diff --git a/packages/docs/app/page.tsx b/packages/docs/app/page.tsx index f2759c8..fba4b66 100644 --- a/packages/docs/app/page.tsx +++ b/packages/docs/app/page.tsx @@ -1,279 +1,219 @@ 'use client'; - import { - Layout, - Section, - Article, - Masthead, - Headline, - Subhead, - Kicker, - BodyText, + Layout, Section, Article, Masthead, Rule, Folio, IndexBox, Factbox, + Headline, Subhead, Kicker, BodyText, Byline, Dateline, + Figure, PullQuote, Footer, BreakingNewsBanner, } from 'newspaperui-components'; import Link from 'next/link'; -const demos = [ - { - href: '/blocks/zh-frontpage', - lang: '中文 · Chinese', - title: '人民周报 · 头版', - description: '思源宋体 + 克制朱红 + 紧凑排版。中文报纸传统视觉语言。', - color: '#CC2929', - }, - { - href: '/blocks/zh-feature', - lang: '中文 · Chinese', - title: '人民周报 · 副刊专题', - description: '深度专题排版:访谈、人物、文化评论。', - color: '#CC2929', - }, - { - href: '/examples/nyt-frontpage', - lang: 'English', - title: 'The Daily Chronicle · NYT Style', - description: - 'Classic American serious newspaper. Cormorant Garamond masthead, multi-column flow with drop cap.', - color: '#1A1A1A', - }, - { - href: '/blocks/en-feature', - lang: 'English', - title: 'The Daily Chronicle · Long-form Feature', - description: 'Editorial long-form piece with pull quotes and editorial design.', - color: '#1A1A1A', - }, - { - href: '/blocks/jp-horizontal', - lang: '日本語 · Japanese', - title: '朝日新聞 · 横組み', - description: 'Modern horizontal Japanese newspaper layout. Noto Serif JP.', - color: '#1B2A4A', - }, - { - href: '/blocks/jp-vertical', - lang: '日本語 · Japanese', - title: '朝日新聞 · 縦組み', - description: 'Traditional vertical writing-mode Japanese layout.', - color: '#1B2A4A', - }, - { - href: '/examples/blackletter-frontpage', - lang: 'Deutsch', - title: 'Die Frankfurter Zeitung', - description: 'Blackletter masthead with UnifrakturMaguntia. German broadsheet tradition.', - color: '#1A1A1A', - }, -]; - export default function LandingPage() { return ( - + + {/* Breaking News Banner */} + + v0.1.0 released — 24 components, multi-language blocks, responsive grid, theme customization + + + {/* Masthead */} -
-
- About - - 生产级报纸布局组件库 - - -

- 参考 InDesign 与经典严肃风排版传统(NYT / The Times / FAZ),基于 24 列栅格、CSS Grid + - Multi-column 双层机制构建。 -

-
-
-
- - Print-grade typography, on the modern web. - - - 18 components, 24-column grid, classic serif typography, real multi-column flow. - -
-
- Quick Start - - Install - - -

- pnpm add newspaperui-components newspaperui-theme -

-
-
- - Documentation → - -
-
-
+ {/* Main Content: 5 + 14 + 5 layout */} +
-
-
-
- Live Demos · Production-grade Examples + {/* Left sidebar: Index + Quick links */} +
+ + + + + Install +
+ pnpm add newspaperui-components newspaperui-theme
+ + + + +
    +
  • 24 React components
  • +
  • CSS Grid + Multi-column
  • +
  • 4 font families
  • +
  • Warm off-white palette
  • +
  • Dark mode support
  • +
  • CJK ready (中/日)
  • +
  • 51 tests passing
  • +
+
+
+ + {/* Center: Lead story */} +
+ Design System · Open Source - Multi-language Newspaper Showcase + Print-Grade Typography Meets the Modern Web - - 7 complete newspaper layouts in Chinese, English, German, and Japanese + + 24 components inspired by InDesign, built for React. Real multi-column flow, drop caps, small caps, old-style figures — the full newspaper toolkit. +
+ By the NewspaperUI Team +
+ +
+ + +

Open Source — NewspaperUI brings the precision of professional newspaper typesetting to web development. Built on a 24-column CSS Grid foundation with CSS Multi-column text flow, it reproduces the dense, information-rich layouts that have defined print journalism for centuries.

+

The library's visual weight system — borrowed from InDesign's paragraph styles — drives all typography from a single data source. Change one value in the visual-weights mapping, and every Headline, Subhead, and BodyText component updates globally. This is design-token architecture taken to its logical conclusion.

+

Typography details that most web frameworks ignore are first-class citizens here: true OpenType small caps (not faked with text-transform), old-style figures that blend with body text, hanging punctuation, proper paragraph indentation (no space between paragraphs, first-line indent from paragraph two), and drop caps that actually work with CSS ::first-letter.

+

The multi-column flow is real CSS columns — text reflows naturally across 2, 3, or 4 columns with hairline rules between them. Pull quotes span all columns. Figures break cleanly. Orphans and widows are controlled. This is how InDesign works, translated to the browser.

+

Four preset themes ship out of the box: Classic NYT (warm off-white, Cormorant Garamond masthead), The Times (ink-blue accent), Modern Dark (warm brown-black), and Swiss Modern (Helvetica-clean). The Create page lets you customize every token and export a CSS file for your project.

+
+ + + Components should be few, precise, and unambiguous. Every prop maps to a real newspaper concept. + + + +

Multi-language support is built in from day one. Chinese layouts use Noto Serif SC with restrained red accents (masthead and kickers only). Japanese layouts support both horizontal (yokogumi) and traditional vertical (tategumi) writing modes. German blackletter mastheads use UnifrakturMaguntia.

+

The responsive system uses CSS media queries injected per-Section, so column counts adapt to viewport width without JavaScript. Font sizes use clamp() for fluid scaling between mobile and desktop. No runtime overhead, no layout shift.

+
+
+ + {/* Right sidebar: Demo previews */} +
+ Live Blocks + Multi-language Demos + + {/* Mini Chinese preview */} + +
+
人民周报
+
历史性贸易协定昨日签署…
+
View Chinese Block →
+
+ + + {/* Mini English preview */} + +
+
The Quiet Collapse of the Middle Shelf
+
How a generation of mid-list authors lost their publishers…
+
View English Block →
+
+ + + {/* Mini Japanese preview */} + +
+
朝日新聞
+
歴史的通商協定が成立…
+
View Japanese Block →
+
+ + + + + {/* Quick links */} + More +
    +
  • All 6 Blocks →
  • +
  • NYT Front Page →
  • +
  • Blackletter →
  • +
  • Theme Creator →
  • +
  • Documentation →
  • +
-
- {demos.map((demo, idx) => ( -
- -
-
-
- {demo.lang} -
-
- Demo #{String(idx + 1).padStart(2, '0')} -
-
-
-

- {demo.title} -

-

- {demo.description} -

-
- View demo → -
-
-
- -
- ))} + {/* Bottom section: Component showcase strip */} +
+
+ +
+ +
+ +
    +
  • Layout
  • +
  • Section
  • +
  • Article
  • +
  • Layer
  • +
  • Masthead
  • +
  • Rule
  • +
  • Footer
  • +
  • Sidebar
  • +
  • BreakingNewsBanner
  • +
+
+
+ +
+ +
    +
  • Headline
  • +
  • Subhead
  • +
  • Kicker
  • +
  • BodyText
  • +
  • Quote
  • +
  • Byline
  • +
  • Dateline
  • +
  • Caption
  • +
  • AuthorCard
  • +
  • JumpLine
  • +
+
+
+ +
+ +
    +
  • Image
  • +
  • Figure
  • +
  • Video
  • +
  • PullQuote
  • +
+
+
+ +
+ +
    +
  • IndexBox
  • +
  • Factbox
  • +
  • RelatedArticles
  • +
+
+
-
-
- Design Philosophy - - Print Tradition, Web Implementation - - -

- NewspaperUI 借鉴 InDesign - 段落样式系统的设计 token 思想,把字体、字号、字重、行高、颜色、间距统一收敛到视觉权重映射表(visualWeights)。组件从单一数据源读取样式,修改一处即全局生效。 -

-

- 排版机制采用 Hybrid 双层:CSS Grid 负责大块布局,CSS Multi-column 负责正文流。这是报纸排版传统在 Web - 上的最佳还原。 -

-
-
-
- Tech Stack - - React 18 · TypeScript 5 · CSS Grid + Multi-column - - -

- 4 packages:newspaperui-themenewspaperui-utils、 - newspaperui-components(18 components)、@newspaperui/docs。 -

-

Built with pnpm workspaces + Turborepo. Vite for libraries, Next.js 15 for docs.

-
-
-
+ {/* Footer */} +