feat: responsive system, engineering infra, new components, performance
- Section: responsive prop with media query injection - visual-weights: fontSize clamp() for responsive sizing - variables.css: add border-radius/shadow/transition/z-index tokens - ESLint flat config + Prettier + Changeset init - New components: Footer, NewsSidebar, BreakingNewsBanner - Image/Figure: loading=lazy, aspectRatio, sizes props
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Changesets
|
||||||
|
|
||||||
|
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||||
|
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||||
|
find the full documentation for it [in our repository](https://github.com/changesets/changesets).
|
||||||
|
|
||||||
|
We have a quick list of common questions to get you started engaging with this project in
|
||||||
|
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
|
||||||
|
"changelog": "@changesets/cli/changelog",
|
||||||
|
"commit": false,
|
||||||
|
"fixed": [],
|
||||||
|
"linked": [],
|
||||||
|
"access": "restricted",
|
||||||
|
"baseBranch": "main",
|
||||||
|
"updateInternalDependencies": "patch",
|
||||||
|
"ignore": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dist
|
||||||
|
out
|
||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
pnpm-lock.yaml
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import react from 'eslint-plugin-react';
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
plugins: { react, 'react-hooks': reactHooks },
|
||||||
|
rules: {
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
|
},
|
||||||
|
settings: { react: { version: 'detect' } },
|
||||||
|
},
|
||||||
|
{ ignores: ['**/dist/**', '**/node_modules/**', '**/.next/**', '**/out/**', 'packages/docs/**'] },
|
||||||
|
);
|
||||||
+10
-2
@@ -2,17 +2,25 @@
|
|||||||
"name": "newspaperui",
|
"name": "newspaperui",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"description": "A newspaper-style UI component library",
|
"description": "A newspaper-style UI component library",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"dev": "turbo run dev",
|
"dev": "turbo run dev",
|
||||||
"lint": "turbo run lint",
|
"lint": "eslint packages/*/src/",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test",
|
||||||
"clean": "turbo run clean && rm -rf node_modules"
|
"clean": "turbo run clean && rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@changesets/cli": "^2.31.0",
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"eslint": "^10.4.0",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
|
"prettier": "^3.8.3",
|
||||||
"turbo": "^2.3.3",
|
"turbo": "^2.3.3",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.59.4"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.4",
|
"packageManager": "pnpm@9.15.4",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@newspaperui/components",
|
"name": "newspaperui-components",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"description": "React components for newspaperui",
|
"description": "Production-grade newspaper layout React components",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@newspaperui/theme": "workspace:*",
|
"newspaperui-theme": "workspace:*",
|
||||||
"@newspaperui/utils": "workspace:*"
|
"newspaperui-utils": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/react": "^16.1.0",
|
"@testing-library/react": "^16.1.0",
|
||||||
@@ -45,5 +45,23 @@
|
|||||||
"vite": "^5.4.11",
|
"vite": "^5.4.11",
|
||||||
"vite-plugin-dts": "^4.3.0",
|
"vite-plugin-dts": "^4.3.0",
|
||||||
"vitest": "^2.1.8"
|
"vitest": "^2.1.8"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "sunzhongyi",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/joisun/newspaperui.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"newspaper",
|
||||||
|
"react",
|
||||||
|
"components",
|
||||||
|
"layout",
|
||||||
|
"typography",
|
||||||
|
"css-grid",
|
||||||
|
"multi-column"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '@newspaperui/theme';
|
import 'newspaperui-theme';
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
export { Layout, useLayout } from './layout/Layout';
|
export { Layout, useLayout } from './layout/Layout';
|
||||||
@@ -13,6 +13,12 @@ export { Masthead } from './layout/Masthead';
|
|||||||
export type { MastheadProps } from './layout/Masthead';
|
export type { MastheadProps } from './layout/Masthead';
|
||||||
export { Rule } from './layout/Rule';
|
export { Rule } from './layout/Rule';
|
||||||
export type { RuleProps } from './layout/Rule';
|
export type { RuleProps } from './layout/Rule';
|
||||||
|
export { Footer } from './layout/Footer';
|
||||||
|
export type { FooterProps } from './layout/Footer';
|
||||||
|
export { Sidebar as NewsSidebar } from './layout/Sidebar';
|
||||||
|
export type { SidebarProps as NewsSidebarProps } from './layout/Sidebar';
|
||||||
|
export { BreakingNewsBanner } from './layout/BreakingNewsBanner';
|
||||||
|
export type { BreakingNewsBannerProps } from './layout/BreakingNewsBanner';
|
||||||
|
|
||||||
// text
|
// text
|
||||||
export { Headline } from './text/Headline';
|
export { Headline } from './text/Headline';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from './Section';
|
import { useSection } from './Section';
|
||||||
|
|
||||||
export interface ArticleProps {
|
export interface ArticleProps {
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
|
export interface BreakingNewsBannerProps {
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BreakingNewsBanner — 突发新闻横幅
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <BreakingNewsBanner label="BREAKING">
|
||||||
|
* Major earthquake strikes coastal region
|
||||||
|
* </BreakingNewsBanner>
|
||||||
|
*/
|
||||||
|
export const BreakingNewsBanner: React.FC<BreakingNewsBannerProps> = ({
|
||||||
|
label = 'BREAKING', className, style, children,
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
className={cx('nui-breaking', className)}
|
||||||
|
role="alert"
|
||||||
|
style={{
|
||||||
|
gridColumn: '1 / -1',
|
||||||
|
background: 'var(--nui-accent-primary)',
|
||||||
|
color: 'var(--nui-bg-page)',
|
||||||
|
padding: 'var(--nui-space-3) var(--nui-space-6)',
|
||||||
|
fontFamily: 'var(--font-family-meta)',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 600,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 'var(--nui-space-4)',
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{
|
||||||
|
fontVariantCaps: 'small-caps',
|
||||||
|
letterSpacing: '0.1em',
|
||||||
|
fontWeight: 700,
|
||||||
|
flexShrink: 0,
|
||||||
|
}}>{label}</span>
|
||||||
|
<span style={{ flex: 1 }}>{children}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
|
export interface FooterProps {
|
||||||
|
copyright?: string;
|
||||||
|
edition?: string;
|
||||||
|
links?: Array<{ label: string; href: string }>;
|
||||||
|
className?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Footer — 报纸页脚/版权信息区
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <Footer copyright="© 2026 The Daily Chronicle" edition="Vol. CXLIX" />
|
||||||
|
*/
|
||||||
|
export const Footer: React.FC<FooterProps> = ({
|
||||||
|
copyright, edition, links, className, style, children,
|
||||||
|
}) => (
|
||||||
|
<footer
|
||||||
|
className={cx('nui-footer nui-small-caps', className)}
|
||||||
|
style={{
|
||||||
|
gridColumn: '1 / -1',
|
||||||
|
borderTop: '2px solid var(--nui-rule-decorative)',
|
||||||
|
paddingTop: 'var(--nui-space-4)',
|
||||||
|
marginTop: 'var(--nui-space-8)',
|
||||||
|
fontFamily: 'var(--font-family-meta)',
|
||||||
|
fontSize: '11px',
|
||||||
|
letterSpacing: '0.06em',
|
||||||
|
color: 'var(--nui-text-muted)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 'var(--nui-space-4)',
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{copyright && <span>{copyright}</span>}
|
||||||
|
{edition && <span style={{ marginLeft: 'var(--nui-space-4)' }}>{edition}</span>}
|
||||||
|
</div>
|
||||||
|
{links && (
|
||||||
|
<nav style={{ display: 'flex', gap: 'var(--nui-space-4)' }}>
|
||||||
|
{links.map(link => (
|
||||||
|
<a key={link.href} href={link.href} style={{ color: 'var(--nui-text-muted)', textDecoration: 'none' }}>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface MastheadProps {
|
export interface MastheadProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface RuleProps {
|
export interface RuleProps {
|
||||||
variant?: 'hairline' | 'double' | 'thick';
|
variant?: 'hairline' | 'double' | 'thick';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { createContext, useContext, ReactNode, CSSProperties } from 'react';
|
import React, { createContext, useContext, useId, ReactNode, CSSProperties } from 'react';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useLayout } from './Layout';
|
import { useLayout } from './Layout';
|
||||||
|
|
||||||
export interface SectionProps {
|
export interface SectionProps {
|
||||||
@@ -8,6 +8,7 @@ export interface SectionProps {
|
|||||||
gap?: string; // 默认 'var(--nui-gutter)'
|
gap?: string; // 默认 'var(--nui-gutter)'
|
||||||
breakable?: boolean; // 是否允许 print 分页断开,默认 true
|
breakable?: boolean; // 是否允许 print 分页断开,默认 true
|
||||||
divider?: 'none' | 'top' | 'bottom' | 'both'; // hairline 分隔
|
divider?: 'none' | 'top' | 'bottom' | 'both'; // hairline 分隔
|
||||||
|
responsive?: { sm?: number; md?: number; lg?: number }; // 响应式列数覆盖
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -32,10 +33,17 @@ export const useSection = () => useContext(SectionContext);
|
|||||||
*/
|
*/
|
||||||
export const Section: React.FC<SectionProps> = ({
|
export const Section: React.FC<SectionProps> = ({
|
||||||
columns, gap = 'var(--nui-gutter)', breakable = true, divider = 'none',
|
columns, gap = 'var(--nui-gutter)', breakable = true, divider = 'none',
|
||||||
className, style, children,
|
responsive, className, style, children,
|
||||||
}) => {
|
}) => {
|
||||||
const layout = useLayout();
|
const layout = useLayout();
|
||||||
const cols = clampSpan(columns, layout.columns);
|
const cols = clampSpan(columns, layout.columns);
|
||||||
|
const id = useId().replace(/:/g, '');
|
||||||
|
const sectionId = `nui-s-${id}`;
|
||||||
|
|
||||||
|
const responsiveCSS = responsive ? `
|
||||||
|
@media (max-width: 768px) { .${sectionId} { grid-template-columns: repeat(${responsive.sm ?? 12}, 1fr) !important; } }
|
||||||
|
@media (min-width: 769px) and (max-width: 1024px) { .${sectionId} { grid-template-columns: repeat(${responsive.md ?? 16}, 1fr) !important; } }
|
||||||
|
` : '';
|
||||||
|
|
||||||
const dividerStyle: CSSProperties = {};
|
const dividerStyle: CSSProperties = {};
|
||||||
if (divider === 'top' || divider === 'both')
|
if (divider === 'top' || divider === 'both')
|
||||||
@@ -45,8 +53,9 @@ export const Section: React.FC<SectionProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContext.Provider value={{ columns: cols }}>
|
<SectionContext.Provider value={{ columns: cols }}>
|
||||||
|
{responsiveCSS && <style dangerouslySetInnerHTML={{ __html: responsiveCSS }} />}
|
||||||
<section
|
<section
|
||||||
className={cx('nui-section', className)}
|
className={cx('nui-section', sectionId, className)}
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
|
import { useSection } from './Section';
|
||||||
|
|
||||||
|
export interface SidebarProps {
|
||||||
|
span?: number;
|
||||||
|
position?: 'left' | 'right';
|
||||||
|
divider?: boolean;
|
||||||
|
className?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar — 侧边栏内容区(天气、股票、快讯摘要等)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <Section columns={24}>
|
||||||
|
* <Article span={18}>...</Article>
|
||||||
|
* <Sidebar span={6} position="right" divider>...</Sidebar>
|
||||||
|
* </Section>
|
||||||
|
*/
|
||||||
|
export const Sidebar: React.FC<SidebarProps> = ({
|
||||||
|
span, position = 'right', divider = false, className, style, children,
|
||||||
|
}) => {
|
||||||
|
const section = useSection();
|
||||||
|
const cols = span ? clampSpan(span, section.columns) : 6;
|
||||||
|
const borderStyle: CSSProperties = divider
|
||||||
|
? position === 'left'
|
||||||
|
? { borderRight: '1px solid var(--nui-rule-hairline)', paddingRight: 'var(--nui-space-4)' }
|
||||||
|
: { borderLeft: '1px solid var(--nui-rule-hairline)', paddingLeft: 'var(--nui-space-4)' }
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside
|
||||||
|
className={cx('nui-sidebar', className)}
|
||||||
|
style={{
|
||||||
|
gridColumn: `span ${cols}`,
|
||||||
|
...borderStyle,
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
import { Caption } from '../text/Caption';
|
import { Caption } from '../text/Caption';
|
||||||
|
|
||||||
export interface FigureProps {
|
export interface FigureProps {
|
||||||
src: string;
|
src?: string;
|
||||||
alt: string;
|
alt?: string;
|
||||||
caption?: string;
|
caption?: string;
|
||||||
credit?: string;
|
credit?: string;
|
||||||
span?: number;
|
span?: number;
|
||||||
|
loading?: 'lazy' | 'eager';
|
||||||
|
aspectRatio?: string;
|
||||||
|
sizes?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,6 +24,7 @@ export interface FigureProps {
|
|||||||
* - 将图片与 Caption 组合为语义化 figure 元素
|
* - 将图片与 Caption 组合为语义化 figure 元素
|
||||||
* - 支持 caption 文字说明和 credit 来源标注
|
* - 支持 caption 文字说明和 credit 来源标注
|
||||||
* - 自动避免 print 分页断开(break-inside: avoid)
|
* - 自动避免 print 分页断开(break-inside: avoid)
|
||||||
|
* - 支持 children 自定义内容替代 img
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* <Figure
|
* <Figure
|
||||||
@@ -28,10 +33,12 @@ export interface FigureProps {
|
|||||||
* caption="Downtown at dusk"
|
* caption="Downtown at dusk"
|
||||||
* credit="Photo by John"
|
* credit="Photo by John"
|
||||||
* span={10}
|
* span={10}
|
||||||
|
* aspectRatio="16/9"
|
||||||
* />
|
* />
|
||||||
*/
|
*/
|
||||||
export const Figure: React.FC<FigureProps> = ({
|
export const Figure: React.FC<FigureProps> = ({
|
||||||
src, alt, caption, credit, span, className, style,
|
src, alt, caption, credit, span, loading, aspectRatio, sizes,
|
||||||
|
className, style, children,
|
||||||
}) => {
|
}) => {
|
||||||
const section = useSection();
|
const section = useSection();
|
||||||
const cols = span ? clampSpan(span, section.columns) : undefined;
|
const cols = span ? clampSpan(span, section.columns) : undefined;
|
||||||
@@ -44,7 +51,15 @@ export const Figure: React.FC<FigureProps> = ({
|
|||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src={src} alt={alt} style={{ display: 'block', width: '100%', height: 'auto' }} />
|
{src ? (
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt={alt ?? ''}
|
||||||
|
loading={loading ?? 'lazy'}
|
||||||
|
sizes={sizes}
|
||||||
|
style={{ display: 'block', width: '100%', height: 'auto', aspectRatio }}
|
||||||
|
/>
|
||||||
|
) : children}
|
||||||
{(caption || credit) && <Caption credit={credit}>{caption}</Caption>}
|
{(caption || credit) && <Caption credit={credit}>{caption}</Caption>}
|
||||||
</figure>
|
</figure>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
export interface ImageProps {
|
export interface ImageProps {
|
||||||
src: string;
|
src: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
span?: number;
|
span?: number;
|
||||||
|
loading?: 'lazy' | 'eager';
|
||||||
|
aspectRatio?: string;
|
||||||
|
sizes?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
@@ -16,23 +19,29 @@ export interface ImageProps {
|
|||||||
*
|
*
|
||||||
* - 响应式全宽图片,自动适配栅格列宽
|
* - 响应式全宽图片,自动适配栅格列宽
|
||||||
* - 通过 span 控制在 Section 中占据的列数
|
* - 通过 span 控制在 Section 中占据的列数
|
||||||
|
* - 默认 lazy loading 优化性能
|
||||||
* - 必须提供 alt 属性以确保无障碍访问
|
* - 必须提供 alt 属性以确保无障碍访问
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* <Image src="/photo.jpg" alt="City skyline" span={12} />
|
* <Image src="/photo.jpg" alt="City skyline" span={12} aspectRatio="16/9" />
|
||||||
*/
|
*/
|
||||||
export const Image: React.FC<ImageProps> = ({ src, alt, span, className, style }) => {
|
export const Image: React.FC<ImageProps> = ({
|
||||||
|
src, alt, span, loading, aspectRatio, sizes, className, style,
|
||||||
|
}) => {
|
||||||
const section = useSection();
|
const section = useSection();
|
||||||
const cols = span ? clampSpan(span, section.columns) : undefined;
|
const cols = span ? clampSpan(span, section.columns) : undefined;
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
|
loading={loading ?? 'lazy'}
|
||||||
|
sizes={sizes}
|
||||||
className={cx('nui-image', className)}
|
className={cx('nui-image', className)}
|
||||||
style={{
|
style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
|
aspectRatio,
|
||||||
gridColumn: cols ? `span ${cols}` : undefined,
|
gridColumn: cols ? `span ${cols}` : undefined,
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
export interface PullQuoteProps {
|
export interface PullQuoteProps {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
import { Caption } from '../text/Caption';
|
import { Caption } from '../text/Caption';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
export interface BodyTextProps {
|
export interface BodyTextProps {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface BylineProps {
|
export interface BylineProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface CaptionProps {
|
export interface CaptionProps {
|
||||||
credit?: string; // e.g. "Photograph by Jane Doe"
|
credit?: string; // e.g. "Photograph by Jane Doe"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface DatelineProps {
|
export interface DatelineProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
const weightToTag: Record<'High' | 'Medium' | 'Low', 'h1' | 'h2' | 'h3'> = {
|
const weightToTag: Record<'High' | 'Medium' | 'Low', 'h1' | 'h2' | 'h3'> = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
export interface KickerProps {
|
export interface KickerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
export interface QuoteProps {
|
export interface QuoteProps {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { ReactNode, CSSProperties } from 'react';
|
import React, { ReactNode, CSSProperties } from 'react';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { clampSpan, cx } from '@newspaperui/utils';
|
import { clampSpan, cx } from 'newspaperui-utils';
|
||||||
import { useSection } from '../layout/Section';
|
import { useSection } from '../layout/Section';
|
||||||
|
|
||||||
export interface SubheadProps {
|
export interface SubheadProps {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Headline,
|
Headline,
|
||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
import { PropsTable } from '@/components/PropsTable';
|
import { PropsTable } from '@/components/PropsTable';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
Masthead,
|
Masthead,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
import { PropsTable } from '@/components/PropsTable';
|
import { PropsTable } from '@/components/PropsTable';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
BodyText,
|
BodyText,
|
||||||
Figure,
|
Figure,
|
||||||
PullQuote,
|
PullQuote,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
import { PropsTable } from '@/components/PropsTable';
|
import { PropsTable } from '@/components/PropsTable';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
Rule,
|
Rule,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
import { PropsTable } from '@/components/PropsTable';
|
import { PropsTable } from '@/components/PropsTable';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
Rule,
|
Rule,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { CodeBlock } from '@/components/CodeBlock';
|
import { CodeBlock } from '@/components/CodeBlock';
|
||||||
|
|
||||||
export default function ResponsivePage() {
|
export default function ResponsivePage() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
Rule,
|
Rule,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
|
|
||||||
export default function SpanningPage() {
|
export default function SpanningPage() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
Headline,
|
Headline,
|
||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
import { PropsTable } from '@/components/PropsTable';
|
import { PropsTable } from '@/components/PropsTable';
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
Byline,
|
Byline,
|
||||||
Dateline,
|
Dateline,
|
||||||
Caption,
|
Caption,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
|
import { visualWeights, resolveFontSize } from 'newspaperui-theme';
|
||||||
import { Demo } from '@/components/Demo';
|
import { Demo } from '@/components/Demo';
|
||||||
|
|
||||||
const longText = `Whitehall officials confirmed late Tuesday that the long-anticipated review of national infrastructure funding will be tabled before the recess. The 248-page document, drafted across three departments, recommends a recalibration of regional priorities and a measured shift toward rail electrification. Critics inside the cabinet caution that the timing risks overshadowing the chancellor's autumn statement, while supporters describe the proposals as the most coherent strategic blueprint in a generation.`;
|
const longText = `Whitehall officials confirmed late Tuesday that the long-anticipated review of national infrastructure funding will be tabled before the recess. The 248-page document, drafted across three departments, recommends a recalibration of regional priorities and a measured shift toward rail electrification. Critics inside the cabinet caution that the timing risks overshadowing the chancellor's autumn statement, while supporters describe the proposals as the most coherent strategic blueprint in a generation.`;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
Headline,
|
Headline,
|
||||||
Subhead,
|
Subhead,
|
||||||
BodyText,
|
BodyText,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import { ThemeToggle } from '@/components/ThemeToggle';
|
import { ThemeToggle } from '@/components/ThemeToggle';
|
||||||
|
|
||||||
interface Swatch {
|
interface Swatch {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Layout, Section, Article, Rule, Headline, Subhead, Kicker, BodyText, Byline, Dateline, Figure, PullQuote } from '@newspaperui/components';
|
import { Layout, Section, Article, Rule, Headline, Subhead, Kicker, BodyText, Byline, Dateline, Figure, PullQuote } from 'newspaperui-components';
|
||||||
|
|
||||||
export default function EnFeature() {
|
export default function EnFeature() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Layout, Section, Article, Rule, BodyText, Figure } from '@newspaperui/components';
|
import { Layout, Section, Article, Rule, BodyText, Figure } from 'newspaperui-components';
|
||||||
|
|
||||||
const jp = { fontFamily: 'var(--font-family-cjk-jp)' };
|
const jp = { fontFamily: 'var(--font-family-cjk-jp)' };
|
||||||
const jpAccent = { color: 'var(--nui-accent-ink-blue)' };
|
const jpAccent = { color: 'var(--nui-accent-ink-blue)' };
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Layout, Section, Article, Headline, Subhead, Kicker } from '@newspaperui/components';
|
import { Layout, Section, Article, Headline, Subhead, Kicker } from 'newspaperui-components';
|
||||||
|
|
||||||
const blocks = [
|
const blocks = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Layout, Section, Article, Rule, BodyText, Quote } from '@newspaperui/components';
|
import { Layout, Section, Article, Rule, BodyText, Quote } from 'newspaperui-components';
|
||||||
|
|
||||||
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
||||||
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Layout, Section, Article, BodyText, Quote, Figure, PullQuote } from '@newspaperui/components';
|
import { Layout, Section, Article, BodyText, Quote, Figure, PullQuote } from 'newspaperui-components';
|
||||||
|
|
||||||
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
||||||
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Layout, Section, Article, Rule, BodyText, Figure } from '@newspaperui/components';
|
import { Layout, Section, Article, Rule, BodyText, Figure } from 'newspaperui-components';
|
||||||
|
|
||||||
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
const cn = { fontFamily: 'var(--font-family-cjk-serif)' };
|
||||||
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
const cnRed = { color: 'var(--nui-accent-cjk-red)' };
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Layout, Section, Article, Masthead,
|
Layout, Section, Article, Masthead,
|
||||||
Headline, Subhead, Kicker, BodyText, Byline, Dateline,
|
Headline, Subhead, Kicker, BodyText, Byline, Dateline,
|
||||||
Figure, PullQuote,
|
Figure, PullQuote,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
|
|
||||||
export default function BlackletterFrontPage() {
|
export default function BlackletterFrontPage() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Layout, Section, Article, Masthead, Rule,
|
Layout, Section, Article, Masthead, Rule,
|
||||||
Headline, Subhead, Kicker, BodyText, Byline, Dateline,
|
Headline, Subhead, Kicker, BodyText, Byline, Dateline,
|
||||||
Figure, PullQuote,
|
Figure, PullQuote,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
|
|
||||||
export default function FrontPage() {
|
export default function FrontPage() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import "@newspaperui/theme/dist/style.css";
|
@import "newspaperui-theme/dist/style.css";
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Subhead,
|
Subhead,
|
||||||
Kicker,
|
Kicker,
|
||||||
BodyText,
|
BodyText,
|
||||||
} from '@newspaperui/components';
|
} from 'newspaperui-components';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
const demos = [
|
const demos = [
|
||||||
@@ -108,7 +108,7 @@ export default function LandingPage() {
|
|||||||
</Headline>
|
</Headline>
|
||||||
<BodyText weight="Medium">
|
<BodyText weight="Medium">
|
||||||
<p style={{ fontFamily: 'var(--font-family-meta)', fontSize: '13px' }}>
|
<p style={{ fontFamily: 'var(--font-family-meta)', fontSize: '13px' }}>
|
||||||
<code>pnpm add @newspaperui/components @newspaperui/theme</code>
|
<code>pnpm add newspaperui-components newspaperui-theme</code>
|
||||||
</p>
|
</p>
|
||||||
</BodyText>
|
</BodyText>
|
||||||
<div style={{ marginTop: '1rem' }}>
|
<div style={{ marginTop: '1rem' }}>
|
||||||
@@ -267,8 +267,8 @@ export default function LandingPage() {
|
|||||||
</Headline>
|
</Headline>
|
||||||
<BodyText weight="Low">
|
<BodyText weight="Low">
|
||||||
<p>
|
<p>
|
||||||
4 packages:<code>@newspaperui/theme</code>、<code>@newspaperui/utils</code>、
|
4 packages:<code>newspaperui-theme</code>、<code>newspaperui-utils</code>、
|
||||||
<code>@newspaperui/components</code>(18 components)、<code>@newspaperui/docs</code>。
|
<code>newspaperui-components</code>(18 components)、<code>@newspaperui/docs</code>。
|
||||||
</p>
|
</p>
|
||||||
<p>Built with pnpm workspaces + Turborepo. Vite for libraries, Next.js 15 for docs.</p>
|
<p>Built with pnpm workspaces + Turborepo. Vite for libraries, Next.js 15 for docs.</p>
|
||||||
</BodyText>
|
</BodyText>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { cx } from '@newspaperui/utils';
|
import { cx } from 'newspaperui-utils';
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const nextConfig = {
|
|||||||
basePath: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
basePath: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
||||||
images: { unoptimized: true },
|
images: { unoptimized: true },
|
||||||
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
|
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
|
||||||
transpilePackages: ['@newspaperui/components', '@newspaperui/theme', '@newspaperui/utils'],
|
transpilePackages: ['newspaperui-components', 'newspaperui-theme', 'newspaperui-utils'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const withMDX = createMDX({
|
const withMDX = createMDX({
|
||||||
|
|||||||
@@ -13,16 +13,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdx-js/loader": "^3.1.1",
|
"@mdx-js/loader": "^3.1.1",
|
||||||
"@mdx-js/react": "^3.1.1",
|
"@mdx-js/react": "^3.1.1",
|
||||||
"@newspaperui/components": "workspace:*",
|
|
||||||
"@newspaperui/theme": "workspace:*",
|
|
||||||
"@newspaperui/utils": "workspace:*",
|
|
||||||
"@next/mdx": "^16.2.6",
|
"@next/mdx": "^16.2.6",
|
||||||
"@types/mdx": "^2.0.13",
|
"@types/mdx": "^2.0.13",
|
||||||
"next": "^15.1.6",
|
"next": "^15.1.6",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"rehype-pretty-code": "^0.14.3",
|
"rehype-pretty-code": "^0.14.3",
|
||||||
"shiki": "^4.1.0"
|
"shiki": "^4.1.0",
|
||||||
|
"newspaperui-components": "workspace:*",
|
||||||
|
"newspaperui-theme": "workspace:*",
|
||||||
|
"newspaperui-utils": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@newspaperui/theme",
|
"name": "newspaperui-theme",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"description": "Theme tokens and CSS variables for newspaperui",
|
"description": "CSS variables, visual weights, and typography utilities for NewspaperUI",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -31,5 +31,21 @@
|
|||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vite": "^5.4.11",
|
"vite": "^5.4.11",
|
||||||
"vite-plugin-dts": "^4.3.0"
|
"vite-plugin-dts": "^4.3.0"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "sunzhongyi",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/joisun/newspaperui.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"newspaper",
|
||||||
|
"typography",
|
||||||
|
"css-variables",
|
||||||
|
"theme",
|
||||||
|
"design-tokens"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,26 @@
|
|||||||
--nui-space-4: 1rem;
|
--nui-space-4: 1rem;
|
||||||
--nui-space-6: 1.5rem;
|
--nui-space-6: 1.5rem;
|
||||||
--nui-space-8: 2rem;
|
--nui-space-8: 2rem;
|
||||||
|
|
||||||
|
/* border-radius */
|
||||||
|
--nui-radius-none: 0;
|
||||||
|
--nui-radius-sm: 2px;
|
||||||
|
--nui-radius-md: 4px;
|
||||||
|
|
||||||
|
/* shadow */
|
||||||
|
--nui-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
--nui-shadow-md: 0 2px 8px rgba(0,0,0,0.08);
|
||||||
|
|
||||||
|
/* transition */
|
||||||
|
--nui-transition-fast: 150ms ease;
|
||||||
|
--nui-transition-normal: 250ms ease;
|
||||||
|
|
||||||
|
/* z-index layers */
|
||||||
|
--nui-z-base: 0;
|
||||||
|
--nui-z-sticky: 10;
|
||||||
|
--nui-z-header: 50;
|
||||||
|
--nui-z-modal: 100;
|
||||||
|
--nui-z-tooltip: 150;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
@@ -57,6 +77,8 @@
|
|||||||
--nui-accent-primary: #C97A6E;
|
--nui-accent-primary: #C97A6E;
|
||||||
--nui-accent-ink-blue: #7E94C2;
|
--nui-accent-ink-blue: #7E94C2;
|
||||||
--nui-highlight: #3A2F18;
|
--nui-highlight: #3A2F18;
|
||||||
|
--nui-shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
|
||||||
|
--nui-shadow-md: 0 2px 8px rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body { background: var(--nui-bg-page); color: var(--nui-text-body); }
|
html, body { background: var(--nui-bg-page); color: var(--nui-text-body); }
|
||||||
|
|||||||
@@ -236,9 +236,17 @@ export const visualWeights: Record<ComponentType, Partial<Record<VisualWeight, V
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Resolve fontSize: tuple → first value (lower bound used by default). */
|
/** Resolve fontSize: tuple → clamp(min, preferred, max) for responsive sizing. */
|
||||||
export function resolveFontSize(value: string | [string, string]): string {
|
export function resolveFontSize(value: string | [string, string]): string {
|
||||||
return Array.isArray(value) ? value[0] : value;
|
if (Array.isArray(value)) {
|
||||||
|
const min = value[0];
|
||||||
|
const max = value[1];
|
||||||
|
const minNum = parseFloat(min);
|
||||||
|
const maxNum = parseFloat(max);
|
||||||
|
const vw = ((minNum + maxNum) / 2 / 16 * 1.5).toFixed(2);
|
||||||
|
return `clamp(${min}, ${vw}vw, ${max})`;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolve span: tuple → first value (lower bound). */
|
/** Resolve span: tuple → first value (lower bound). */
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@newspaperui/utils",
|
"name": "newspaperui-utils",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"description": "Utility functions for newspaperui",
|
"description": "Grid validation and utility functions for NewspaperUI",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -29,5 +29,19 @@
|
|||||||
"vite": "^5.4.11",
|
"vite": "^5.4.11",
|
||||||
"vite-plugin-dts": "^4.3.0",
|
"vite-plugin-dts": "^4.3.0",
|
||||||
"vitest": "^2.1.8"
|
"vitest": "^2.1.8"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "sunzhongyi",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/joisun/newspaperui.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"newspaper",
|
||||||
|
"grid",
|
||||||
|
"utilities"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+2345
-17
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user