This commit is contained in:
sunzhongyi
2026-05-20 01:30:49 +08:00
parent 7dded89537
commit 610805a374
42 changed files with 3451 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
{"sessionId":"9eea8633-2610-41cb-8b8f-1223ddb5e487","pid":60045,"procStart":"Tue May 19 12:24:21 2026","acquiredAt":1779203525468}
@@ -0,0 +1,74 @@
- generic [active] [ref=e1]:
- generic [ref=e2]:
- banner [ref=e3]:
- generic [ref=e5]: Late City Edition
- heading "The Daily Chronicle" [level=1] [ref=e6]
- generic [ref=e7]:
- generic [ref=e8]: Vol. CXLIX · No. 51,895
- generic [ref=e9]: Tuesday, May 19, 2026
- generic [ref=e10]: $4.00
- generic [ref=e12]:
- article [ref=e13]:
- generic [ref=e14]: Inside Today
- heading "Senate Approves Climate Resolution After Months of Debate" [level=3] [ref=e15]
- paragraph [ref=e17]: The unanimous vote concludes a contentious legislative session marked by partisan disputes and last-minute amendments. Page A6.
- separator [ref=e18]
- heading "Tech Sector Gains as Inflation Eases" [level=3] [ref=e19]
- paragraph [ref=e21]: Major indices climbed for a fifth consecutive session as new data showed price growth slowing across consumer goods. Business B1.
- separator [ref=e22]
- heading "Drought Conditions Worsen Across the Plains" [level=3] [ref=e23]
- paragraph [ref=e25]: Officials in seven states have requested federal disaster relief as reservoir levels reach historic lows. National A12.
- separator [ref=e26]
- heading "New Exhibit Opens at the Metropolitan" [level=3] [ref=e27]
- paragraph [ref=e29]: A retrospective of mid-century textile design draws record opening crowds. Arts C3.
- article [ref=e30]:
- generic [ref=e32]: Capitol · Breaking
- heading "Historic Accord Reshapes Continental Trade After Marathon Session" [level=1] [ref=e33]
- paragraph [ref=e34]: Negotiators emerge with sweeping framework on tariffs, labor, and emissions; ratification expected within weeks
- generic [ref=e35]:
- generic [ref=e36]: By Eleanor Whitcombe and Marcus Reyes
- generic [ref=e37]: ·
- generic [ref=e38]: 5 min read
- figure "Negotiators applaud after the final draft was approved Monday evening at the Continental Conference Center. Photograph by Jane Doe / Pool" [ref=e39]:
- img "Diplomats applaud after the final draft was approved" [ref=e40]
- generic [ref=e41]:
- text: Negotiators applaud after the final draft was approved Monday evening at the Continental Conference Center.
- generic [ref=e42]: Photograph by Jane Doe / Pool
- generic [ref=e43]:
- paragraph [ref=e44]: Brussels — After eleven consecutive days of negotiation that several participants described as the most demanding in a generation, delegates from twenty-three nations announced on Monday a sweeping framework to reorganize commerce across the continent. The accord, which still requires ratification by member parliaments, would harmonize tariff schedules, set common labor standards, and bind signatories to a shared emissions pathway through 2040.
- paragraph [ref=e45]: Officials briefed on the talks said the breakthrough came shortly before midnight, when a dispute over agricultural subsidies was resolved with a side letter granting transitional relief to producers in five smaller economies. The chief negotiator, Margarethe Lindqvist, called the outcome “a long argument that finally became a conversation.”
- paragraph [ref=e46]: The frameworks most consequential provisions target heavy industry. Cement, steel, and chemical producers would face a graduated carbon levy beginning in 2028, with revenues recycled into a continental investment fund for low-carbon manufacturing. Industry associations expressed cautious support, while environmental groups praised the levys binding architecture but warned that the timeline gives polluters too much room to delay.
- paragraph [ref=e47]: Markets reacted with measured optimism. The continental composite index closed up 1.2 percent, led by capital-goods makers expected to benefit from infrastructure investment. The currency strengthened against the dollar by 0.7 percent. Bond yields, which had climbed throughout the negotiations on fiscal-stability concerns, retreated to levels seen before the talks began.
- paragraph [ref=e48]: Domestic political reaction was mixed. The accords labor provisions, which establish minimum standards for paid leave and collective bargaining, drew immediate praise from union federations and equally immediate concern from chambers of commerce. The chairman of the Federation of Industries warned that small firms would struggle with compliance costs absent transitional support.
- paragraph [ref=e49]: Parliamentary leaders in three capitals signaled that ratification could occur before the summer recess. Two governments, however, indicated that they would seek public referenda before committing, a process likely to extend into the autumn. Analysts at the Centre for Trade Studies estimated that full implementation, even on the most expedited timeline, would require at least eighteen months.
- paragraph [ref=e50]: "For ordinary travelers and consumers, the immediate effects will be modest. Border procedures and product standards remain governed by existing arrangements pending ratification. The longer arc is what matters: a continent of historically fractious neighbors agreeing on a single set of rules for the most consequential decade in living memory."
- complementary [ref=e51]:
- paragraph [ref=e52]: A long argument that finally became a conversation.
- generic [ref=e53]: — Margarethe Lindqvist, Chief Negotiator
- generic [ref=e54]:
- paragraph [ref=e55]: The accords signing ceremony, originally scheduled for last Friday, was delayed three times as drafters reconciled competing texts on dispute resolution. The final compromise establishes an arbitration panel of nine jurists, three appointed by each of the blocs three regional groupings, with binding authority over commercial disputes exceeding twenty million units.
- paragraph [ref=e56]: Critics on the populist right denounced the framework as an erosion of national sovereignty, while critics on the left argued that the labor floor was set too low to meaningfully protect workers in tighter regulatory regimes. Both camps signaled that ratification battles would be fierce, particularly in legislatures with narrow majorities.
- article [ref=e57]:
- generic [ref=e58]: Foreign Desk
- heading "Coastal Nations Pledge Joint Action on Maritime Pollution" [level=2] [ref=e59]
- paragraph [ref=e60]: Pact follows years of stalled regional talks and a cascade of recent shipping accidents.
- generic [ref=e61]: By Tomás Almeida
- generic [ref=e62]:
- paragraph [ref=e63]: Lisbon — Eleven coastal nations announced a binding compact to coordinate cleanup operations and harmonize liability rules for vessels exceeding fifty thousand tons. The agreement establishes a shared rapid-response fund and creates a regional inspectorate empowered to detain non-compliant ships in any signatory port.
- paragraph [ref=e64]: Maritime industry groups received the news with caution. A spokesperson for the Continental Shipping Council acknowledged that “stronger common rules are overdue” but warned that implementation costs could fall disproportionately on smaller operators.
- paragraph [ref=e65]: The compact takes effect on January 1, pending technical annexes. Environmental observers described the pact as the most consequential maritime accord in a decade.
- article [ref=e67]:
- generic [ref=e68]: National · Investigation
- heading "Records Reveal Years of Overlooked Warnings at Aging Reservoirs" [level=2] [ref=e69]
- paragraph [ref=e70]: Internal inspection memoranda, obtained through public records requests, suggest that structural concerns flagged repeatedly by field engineers were not escalated to senior staff.
- generic [ref=e71]: By Ravi Nair, Anita Kowalski, and Charles Weston
- generic [ref=e72]:
- paragraph [ref=e73]: Sacramento — A six-month review of more than four thousand pages of inspection records, interviews with twenty-three current and former engineers, and reconstructions of three near-failure incidents reveals a pattern of unheeded warnings about the structural integrity of mid-twentieth-century earthen dams across the western states.
- paragraph [ref=e74]: The records show that field engineers documented concerns about seepage, erosion, and spillway capacity in repeated annual assessments dating back at least fifteen years. In several instances, those concerns were rated “moderate” in the field reports but downgraded to “low” by the time they reached senior officials. The pattern was particularly pronounced at three facilities serving regions of more than two million residents.
- paragraph [ref=e75]: Officials at the Department of Water Resources, asked to review excerpts of the records, said in a written statement that “every reservoir under our oversight has been deemed safe for current operations” but did not specifically address the discrepancies between field and final ratings. The agency declined to make senior staff available for interviews.
- paragraph [ref=e76]: The findings come amid renewed scrutiny of aging infrastructure following the partial collapse of an earthen embankment in March that displaced more than fifteen hundred residents. Federal inspectors who responded to that incident found the proximate cause to be precisely the type of seepage concern that field engineers had flagged in three of the past four annual assessments.
- paragraph [ref=e77]: The investigative review found that of forty-seven reservoirs surveyed, sixteen had at least one instance in which a “moderate” or “high” field rating was downgraded before reaching senior management. In nine cases, the downgrades persisted for three or more consecutive years. None of the affected facilities have publicly disclosed the discrepancies.
- paragraph [ref=e78]: Engineering professional associations have, in recent years, called for an independent review of inspection workflows in the western states. A spokesperson for the Society of Hydraulic Engineers said the Society was “deeply concerned” by the patterns described and would convene a working group to examine reform options.
- button "Open Next.js Dev Tools" [ref=e84] [cursor=pointer]:
- img [ref=e85]
- alert [ref=e88]
@@ -0,0 +1,74 @@
- generic [active] [ref=e1]:
- generic [ref=e2]:
- banner [ref=e3]:
- generic [ref=e5]: Late City Edition
- heading "The Daily Chronicle" [level=1] [ref=e6]
- generic [ref=e7]:
- generic [ref=e8]: Vol. CXLIX · No. 51,895
- generic [ref=e9]: Tuesday, May 19, 2026
- generic [ref=e10]: $4.00
- generic [ref=e12]:
- article [ref=e13]:
- generic [ref=e14]: Inside Today
- heading "Senate Approves Climate Resolution After Months of Debate" [level=3] [ref=e15]
- paragraph [ref=e17]: The unanimous vote concludes a contentious legislative session marked by partisan disputes and last-minute amendments. Page A6.
- separator [ref=e18]
- heading "Tech Sector Gains as Inflation Eases" [level=3] [ref=e19]
- paragraph [ref=e21]: Major indices climbed for a fifth consecutive session as new data showed price growth slowing across consumer goods. Business B1.
- separator [ref=e22]
- heading "Drought Conditions Worsen Across the Plains" [level=3] [ref=e23]
- paragraph [ref=e25]: Officials in seven states have requested federal disaster relief as reservoir levels reach historic lows. National A12.
- separator [ref=e26]
- heading "New Exhibit Opens at the Metropolitan" [level=3] [ref=e27]
- paragraph [ref=e29]: A retrospective of mid-century textile design draws record opening crowds. Arts C3.
- article [ref=e30]:
- generic [ref=e32]: Capitol · Breaking
- heading "Historic Accord Reshapes Continental Trade After Marathon Session" [level=1] [ref=e33]
- paragraph [ref=e34]: Negotiators emerge with sweeping framework on tariffs, labor, and emissions; ratification expected within weeks
- generic [ref=e35]:
- generic [ref=e36]: By Eleanor Whitcombe and Marcus Reyes
- generic [ref=e37]: ·
- generic [ref=e38]: 5 min read
- figure "Negotiators applaud after the final draft was approved Monday evening at the Continental Conference Center. Photograph by Jane Doe / Pool" [ref=e39]:
- img "Diplomats applaud after the final draft was approved" [ref=e40]
- generic [ref=e41]:
- text: Negotiators applaud after the final draft was approved Monday evening at the Continental Conference Center.
- generic [ref=e42]: Photograph by Jane Doe / Pool
- generic [ref=e43]:
- paragraph [ref=e44]: Brussels — After eleven consecutive days of negotiation that several participants described as the most demanding in a generation, delegates from twenty-three nations announced on Monday a sweeping framework to reorganize commerce across the continent. The accord, which still requires ratification by member parliaments, would harmonize tariff schedules, set common labor standards, and bind signatories to a shared emissions pathway through 2040.
- paragraph [ref=e45]: Officials briefed on the talks said the breakthrough came shortly before midnight, when a dispute over agricultural subsidies was resolved with a side letter granting transitional relief to producers in five smaller economies. The chief negotiator, Margarethe Lindqvist, called the outcome “a long argument that finally became a conversation.”
- paragraph [ref=e46]: The frameworks most consequential provisions target heavy industry. Cement, steel, and chemical producers would face a graduated carbon levy beginning in 2028, with revenues recycled into a continental investment fund for low-carbon manufacturing. Industry associations expressed cautious support, while environmental groups praised the levys binding architecture but warned that the timeline gives polluters too much room to delay.
- paragraph [ref=e47]: Markets reacted with measured optimism. The continental composite index closed up 1.2 percent, led by capital-goods makers expected to benefit from infrastructure investment. The currency strengthened against the dollar by 0.7 percent. Bond yields, which had climbed throughout the negotiations on fiscal-stability concerns, retreated to levels seen before the talks began.
- paragraph [ref=e48]: Domestic political reaction was mixed. The accords labor provisions, which establish minimum standards for paid leave and collective bargaining, drew immediate praise from union federations and equally immediate concern from chambers of commerce. The chairman of the Federation of Industries warned that small firms would struggle with compliance costs absent transitional support.
- paragraph [ref=e49]: Parliamentary leaders in three capitals signaled that ratification could occur before the summer recess. Two governments, however, indicated that they would seek public referenda before committing, a process likely to extend into the autumn. Analysts at the Centre for Trade Studies estimated that full implementation, even on the most expedited timeline, would require at least eighteen months.
- paragraph [ref=e50]: "For ordinary travelers and consumers, the immediate effects will be modest. Border procedures and product standards remain governed by existing arrangements pending ratification. The longer arc is what matters: a continent of historically fractious neighbors agreeing on a single set of rules for the most consequential decade in living memory."
- complementary [ref=e51]:
- paragraph [ref=e52]: A long argument that finally became a conversation.
- generic [ref=e53]: — Margarethe Lindqvist, Chief Negotiator
- generic [ref=e54]:
- paragraph [ref=e55]: The accords signing ceremony, originally scheduled for last Friday, was delayed three times as drafters reconciled competing texts on dispute resolution. The final compromise establishes an arbitration panel of nine jurists, three appointed by each of the blocs three regional groupings, with binding authority over commercial disputes exceeding twenty million units.
- paragraph [ref=e56]: Critics on the populist right denounced the framework as an erosion of national sovereignty, while critics on the left argued that the labor floor was set too low to meaningfully protect workers in tighter regulatory regimes. Both camps signaled that ratification battles would be fierce, particularly in legislatures with narrow majorities.
- article [ref=e57]:
- generic [ref=e58]: Foreign Desk
- heading "Coastal Nations Pledge Joint Action on Maritime Pollution" [level=2] [ref=e59]
- paragraph [ref=e60]: Pact follows years of stalled regional talks and a cascade of recent shipping accidents.
- generic [ref=e61]: By Tomás Almeida
- generic [ref=e62]:
- paragraph [ref=e63]: Lisbon — Eleven coastal nations announced a binding compact to coordinate cleanup operations and harmonize liability rules for vessels exceeding fifty thousand tons. The agreement establishes a shared rapid-response fund and creates a regional inspectorate empowered to detain non-compliant ships in any signatory port.
- paragraph [ref=e64]: Maritime industry groups received the news with caution. A spokesperson for the Continental Shipping Council acknowledged that “stronger common rules are overdue” but warned that implementation costs could fall disproportionately on smaller operators.
- paragraph [ref=e65]: The compact takes effect on January 1, pending technical annexes. Environmental observers described the pact as the most consequential maritime accord in a decade.
- article [ref=e67]:
- generic [ref=e68]: National · Investigation
- heading "Records Reveal Years of Overlooked Warnings at Aging Reservoirs" [level=2] [ref=e69]
- paragraph [ref=e70]: Internal inspection memoranda, obtained through public records requests, suggest that structural concerns flagged repeatedly by field engineers were not escalated to senior staff.
- generic [ref=e71]: By Ravi Nair, Anita Kowalski, and Charles Weston
- generic [ref=e72]:
- paragraph [ref=e73]: Sacramento — A six-month review of more than four thousand pages of inspection records, interviews with twenty-three current and former engineers, and reconstructions of three near-failure incidents reveals a pattern of unheeded warnings about the structural integrity of mid-twentieth-century earthen dams across the western states.
- paragraph [ref=e74]: The records show that field engineers documented concerns about seepage, erosion, and spillway capacity in repeated annual assessments dating back at least fifteen years. In several instances, those concerns were rated “moderate” in the field reports but downgraded to “low” by the time they reached senior officials. The pattern was particularly pronounced at three facilities serving regions of more than two million residents.
- paragraph [ref=e75]: Officials at the Department of Water Resources, asked to review excerpts of the records, said in a written statement that “every reservoir under our oversight has been deemed safe for current operations” but did not specifically address the discrepancies between field and final ratings. The agency declined to make senior staff available for interviews.
- paragraph [ref=e76]: The findings come amid renewed scrutiny of aging infrastructure following the partial collapse of an earthen embankment in March that displaced more than fifteen hundred residents. Federal inspectors who responded to that incident found the proximate cause to be precisely the type of seepage concern that field engineers had flagged in three of the past four annual assessments.
- paragraph [ref=e77]: The investigative review found that of forty-seven reservoirs surveyed, sixteen had at least one instance in which a “moderate” or “high” field rating was downgraded before reaching senior management. In nine cases, the downgrades persisted for three or more consecutive years. None of the affected facilities have publicly disclosed the discrepancies.
- paragraph [ref=e78]: Engineering professional associations have, in recent years, called for an independent review of inspection workflows in the western states. A spokesperson for the Society of Hydraulic Engineers said the Society was “deeply concerned” by the patterns described and would convene a working group to examine reform options.
- button "Open Next.js Dev Tools" [ref=e84] [cursor=pointer]:
- img [ref=e85]
- alert [ref=e88]
@@ -0,0 +1,39 @@
- generic [active] [ref=e1]:
- generic [ref=e2]:
- banner [ref=e3]:
- heading "Die Frankfurter Zeitung" [level=1] [ref=e5]
- generic [ref=e6]:
- generic [ref=e7]: Nr. 117 · 142. Jahrgang
- generic [ref=e8]: Dienstag, 19. Mai 2026
- generic [ref=e9]: € 3,80
- generic [ref=e11]:
- article [ref=e12]:
- generic [ref=e13]: Im Blatt
- heading "Bundestag billigt Klimapaket nach langer Debatte" [level=3] [ref=e14]
- paragraph [ref=e16]: Die einstimmige Abstimmung beendet eine umstrittene Sitzungsperiode. Politik, Seite 4.
- separator [ref=e17]
- heading "Industrie sieht in Reform Chance und Risiko" [level=3] [ref=e18]
- paragraph [ref=e20]: Verbände begrüßen die Pläne, mahnen aber Übergangsfristen für kleinere Betriebe an. Wirtschaft, Seite 9.
- article [ref=e21]:
- generic [ref=e22]: Politik · Eilmeldung
- heading "Historischer Pakt nach langem Verhandlungsmarathon beschlossen" [level=1] [ref=e23]
- paragraph [ref=e24]: Delegierte aus dreiundzwanzig Nationen einigen sich auf einen Rahmen für Zölle, Arbeit und Emissionen
- generic [ref=e25]: Von Eleonore Witkomm und Markus Reyes
- 'figure "Die Delegationen applaudieren nach der Verabschiedung des Schlussdokuments am Montagabend. Foto: Jane Doe / Pool" [ref=e26]':
- img "Verhandlungsführer am Konferenztisch" [ref=e27]
- generic [ref=e28]:
- text: Die Delegationen applaudieren nach der Verabschiedung des Schlussdokuments am Montagabend.
- generic [ref=e29]: "Foto: Jane Doe / Pool"
- generic [ref=e30]:
- paragraph [ref=e31]: Brüssel — Nach elf aufeinanderfolgenden Verhandlungstagen, die mehrere Teilnehmer als die anstrengendsten einer Generation bezeichneten, haben Delegierte aus dreiundzwanzig Nationen am Montag einen umfassenden Rahmen vorgelegt, der den Handel auf dem gesamten Kontinent neu ordnen soll.
- paragraph [ref=e32]: Das Abkommen, das noch von den nationalen Parlamenten ratifiziert werden muss, würde Zollordnungen harmonisieren, gemeinsame Arbeitsstandards setzen und die Unterzeichner auf einen geteilten Emissionspfad bis 2040 verpflichten. Beamte, die in die Gespräche eingeweiht waren, sagten, der Durchbruch sei kurz vor Mitternacht gekommen.
- paragraph [ref=e33]: Die Märkte reagierten mit verhaltenem Optimismus. Der kontinentale Composite-Index schloss mit einem Plus von 1,2 Prozent. Die Währung legte gegenüber dem Dollar um 0,7 Prozent zu. Anleiherenditen, die während der Verhandlungen wegen fiskalischer Sorgen gestiegen waren, kehrten auf das Niveau vor Beginn der Gespräche zurück.
- complementary [ref=e34]:
- paragraph [ref=e35]: Ein langer Streit, der schließlich zum Gespräch wurde.
- generic [ref=e36]: — Margarethe Lindqvist, Chefverhandlerin
- generic [ref=e37]:
- paragraph [ref=e38]: Das Rahmenabkommen sieht eine schrittweise CO₂-Abgabe für Schwerindustrie ab 2028 vor. Einnahmen sollen in einen Investitionsfonds für klimafreundliche Fertigung fließen.
- paragraph [ref=e39]: Parlamentarische Führer in drei Hauptstädten signalisierten, dass die Ratifizierung noch vor der Sommerpause erfolgen könnte. Zwei Regierungen kündigten an, vorher Volksabstimmungen abhalten zu wollen.
- button "Open Next.js Dev Tools" [ref=e45] [cursor=pointer]:
- img [ref=e46]
- alert [ref=e49]
@@ -0,0 +1,39 @@
- generic [active] [ref=e1]:
- generic [ref=e2]:
- banner [ref=e3]:
- heading "Die Frankfurter Zeitung" [level=1] [ref=e5]
- generic [ref=e6]:
- generic [ref=e7]: Nr. 117 · 142. Jahrgang
- generic [ref=e8]: Dienstag, 19. Mai 2026
- generic [ref=e9]: € 3,80
- generic [ref=e11]:
- article [ref=e12]:
- generic [ref=e13]: Im Blatt
- heading "Bundestag billigt Klimapaket nach langer Debatte" [level=3] [ref=e14]
- paragraph [ref=e16]: Die einstimmige Abstimmung beendet eine umstrittene Sitzungsperiode. Politik, Seite 4.
- separator [ref=e17]
- heading "Industrie sieht in Reform Chance und Risiko" [level=3] [ref=e18]
- paragraph [ref=e20]: Verbände begrüßen die Pläne, mahnen aber Übergangsfristen für kleinere Betriebe an. Wirtschaft, Seite 9.
- article [ref=e21]:
- generic [ref=e22]: Politik · Eilmeldung
- heading "Historischer Pakt nach langem Verhandlungsmarathon beschlossen" [level=1] [ref=e23]
- paragraph [ref=e24]: Delegierte aus dreiundzwanzig Nationen einigen sich auf einen Rahmen für Zölle, Arbeit und Emissionen
- generic [ref=e25]: Von Eleonore Witkomm und Markus Reyes
- 'figure "Die Delegationen applaudieren nach der Verabschiedung des Schlussdokuments am Montagabend. Foto: Jane Doe / Pool" [ref=e26]':
- img "Verhandlungsführer am Konferenztisch" [ref=e27]
- generic [ref=e28]:
- text: Die Delegationen applaudieren nach der Verabschiedung des Schlussdokuments am Montagabend.
- generic [ref=e29]: "Foto: Jane Doe / Pool"
- generic [ref=e30]:
- paragraph [ref=e31]: Brüssel — Nach elf aufeinanderfolgenden Verhandlungstagen, die mehrere Teilnehmer als die anstrengendsten einer Generation bezeichneten, haben Delegierte aus dreiundzwanzig Nationen am Montag einen umfassenden Rahmen vorgelegt, der den Handel auf dem gesamten Kontinent neu ordnen soll.
- paragraph [ref=e32]: Das Abkommen, das noch von den nationalen Parlamenten ratifiziert werden muss, würde Zollordnungen harmonisieren, gemeinsame Arbeitsstandards setzen und die Unterzeichner auf einen geteilten Emissionspfad bis 2040 verpflichten. Beamte, die in die Gespräche eingeweiht waren, sagten, der Durchbruch sei kurz vor Mitternacht gekommen.
- paragraph [ref=e33]: Die Märkte reagierten mit verhaltenem Optimismus. Der kontinentale Composite-Index schloss mit einem Plus von 1,2 Prozent. Die Währung legte gegenüber dem Dollar um 0,7 Prozent zu. Anleiherenditen, die während der Verhandlungen wegen fiskalischer Sorgen gestiegen waren, kehrten auf das Niveau vor Beginn der Gespräche zurück.
- complementary [ref=e34]:
- paragraph [ref=e35]: Ein langer Streit, der schließlich zum Gespräch wurde.
- generic [ref=e36]: — Margarethe Lindqvist, Chefverhandlerin
- generic [ref=e37]:
- paragraph [ref=e38]: Das Rahmenabkommen sieht eine schrittweise CO₂-Abgabe für Schwerindustrie ab 2028 vor. Einnahmen sollen in einen Investitionsfonds für klimafreundliche Fertigung fließen.
- paragraph [ref=e39]: Parlamentarische Führer in drei Hauptstädten signalisierten, dass die Ratifizierung noch vor der Sommerpause erfolgen könnte. Zwei Regierungen kündigten an, vorher Volksabstimmungen abhalten zu wollen.
- button "Open Next.js Dev Tools" [ref=e45] [cursor=pointer]:
- img [ref=e46]
- alert [ref=e49]
@@ -0,0 +1,309 @@
- generic [ref=e2]:
- complementary [ref=e3]:
- link "NewspaperUI" [ref=e4] [cursor=pointer]:
- /url: /
- generic [ref=e5]: Production Newspaper Components
- navigation [ref=e6]:
- list [ref=e7]:
- listitem [ref=e8]:
- link "概览 / 头版" [ref=e9] [cursor=pointer]:
- /url: /
- listitem [ref=e10]:
- link "栅格系统" [ref=e11] [cursor=pointer]:
- /url: /grid-system
- listitem [ref=e12]:
- link "布局组件" [ref=e13] [cursor=pointer]:
- /url: /components/article
- list [ref=e14]:
- listitem [ref=e15]:
- link "Masthead" [ref=e16] [cursor=pointer]:
- /url: /components/masthead
- listitem [ref=e17]:
- link "Article + Layer" [ref=e18] [cursor=pointer]:
- /url: /components/article
- listitem [ref=e19]:
- link "Rule 分隔线" [ref=e20] [cursor=pointer]:
- /url: /components/rule
- listitem [ref=e21]:
- link "文本组件" [ref=e22] [cursor=pointer]:
- /url: /text
- listitem [ref=e23]:
- link "媒体组件" [ref=e24] [cursor=pointer]:
- /url: /components/media
- listitem [ref=e25]:
- link "主题与颜色" [ref=e26] [cursor=pointer]:
- /url: /theme
- listitem [ref=e27]:
- link "示例" [ref=e28] [cursor=pointer]:
- /url: /examples/spanning
- list [ref=e29]:
- listitem [ref=e30]:
- link "跨栏布局" [ref=e31] [cursor=pointer]:
- /url: /examples/spanning
- listitem [ref=e32]:
- link "响应式" [ref=e33] [cursor=pointer]:
- /url: /examples/responsive
- listitem [ref=e34]:
- link "Blackletter 头版" [ref=e35] [cursor=pointer]:
- /url: /examples/blackletter-frontpage
- main [ref=e36]:
- article [ref=e39]:
- heading "文本组件" [level=1] [ref=e40]
- paragraph [ref=e41]: 从 Headline 到 Caption 的完整文本谱系。每个组件按视觉权重映射到字体、字号、字重、 行高与颜色 token,避免硬编码样式。
- heading "Headline 三档" [level=2] [ref=e42]
- generic [ref=e43]:
- heading "High / Medium / Low" [level=3] [ref=e45]
- generic [ref=e47]:
- generic [ref=e48]:
- generic [ref=e49]: weight="High"
- heading "A Quiet Revolution in the Treasury Forecast" [level=3] [ref=e50]
- generic [ref=e51]:
- generic [ref=e52]: weight="Medium"
- heading "Whitehall Confirms Infrastructure Review" [level=3] [ref=e53]
- generic [ref=e54]:
- generic [ref=e55]: weight="Low"
- 'heading "Briefing: regional rail commitments" [level=3] [ref=e56]'
- heading "Subhead 两档" [level=2] [ref=e57]
- generic [ref=e58]:
- heading "High / Medium" [level=3] [ref=e60]
- generic [ref=e62]:
- paragraph [ref=e63]: A measured recalibration of regional priorities is expected to define the chancellors autumn agenda.
- paragraph [ref=e64]: Officials emphasized that the headline figures should be read as a planning envelope rather than a binding allocation.
- heading "Kicker" [level=2] [ref=e65]
- generic [ref=e66]:
- heading "朱红 small-caps,挂在 Headline 上方" [level=3] [ref=e68]
- generic [ref=e70]:
- generic [ref=e71]: POLITICS · WHITEHALL
- heading "A Standing Technical Commission, Quietly Proposed" [level=3] [ref=e72]
- heading "BodyText 多栏文字流" [level=2] [ref=e73]
- generic [ref=e74]:
- generic [ref=e75]:
- heading "单栏" [level=3] [ref=e76]
- paragraph [ref=e77]: 默认形态,适合中等宽度的文章正文。
- generic [ref=e79]:
- paragraph [ref=e80]: 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.
- paragraph [ref=e81]: Markets responded cautiously through the morning session. The pound traded sideways against the dollar, gilts firmed by three basis points, and the FTSE 100 closed marginally lower as defensive sectors absorbed the day's modest outflows. Analysts at three of the City's largest houses framed the move as a holding pattern, awaiting further detail on the spending envelope rather than a verdict on its substance.
- generic [ref=e82]:
- generic [ref=e83]:
- heading "三栏 + 首字下沉" [level=3] [ref=e84]
- paragraph [ref=e85]: "开启 columns={3} 与 dropCap,第一段首字母自动下沉占 2-3 行。"
- generic [ref=e87]:
- paragraph [ref=e88]: 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.
- paragraph [ref=e89]: Markets responded cautiously through the morning session. The pound traded sideways against the dollar, gilts firmed by three basis points, and the FTSE 100 closed marginally lower as defensive sectors absorbed the day's modest outflows. Analysts at three of the City's largest houses framed the move as a holding pattern, awaiting further detail on the spending envelope rather than a verdict on its substance.
- paragraph [ref=e90]: In Manchester, regional officials greeted the announcement with measured optimism. "We have lobbied for this kind of clarity for the better part of a decade," said one senior figure who requested anonymity to discuss internal deliberations. The mayor's office is expected to issue a formal response by week's end, focusing on commitments to the trans-Pennine corridor and the long-deferred upgrade of suburban tram capacity.
- paragraph [ref=e91]: "Beyond the immediate fiscal arithmetic, the review's most consequential proposal may be its quietest: a standing technical commission, modeled on Australia's Infrastructure Australia, to depoliticize project sequencing. Whether that body acquires teeth — or settles into the advisory torpor that has consumed earlier attempts — will be determined by the legislation expected in the spring."
- button "查看代码" [ref=e93] [cursor=pointer]
- generic [ref=e94]:
- generic [ref=e95]:
- heading "两栏(无 dropCap" [level=3] [ref=e96]
- paragraph [ref=e97]: "columns={2},栏间细线由 nui-column-rule 提供。"
- generic [ref=e99]:
- paragraph [ref=e100]: 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.
- paragraph [ref=e101]: Markets responded cautiously through the morning session. The pound traded sideways against the dollar, gilts firmed by three basis points, and the FTSE 100 closed marginally lower as defensive sectors absorbed the day's modest outflows. Analysts at three of the City's largest houses framed the move as a holding pattern, awaiting further detail on the spending envelope rather than a verdict on its substance.
- paragraph [ref=e102]: In Manchester, regional officials greeted the announcement with measured optimism. "We have lobbied for this kind of clarity for the better part of a decade," said one senior figure who requested anonymity to discuss internal deliberations. The mayor's office is expected to issue a formal response by week's end, focusing on commitments to the trans-Pennine corridor and the long-deferred upgrade of suburban tram capacity.
- heading "Quote 引用" [level=2] [ref=e103]
- generic [ref=e104]:
- heading "block / inline 对照" [level=3] [ref=e106]
- generic [ref=e108]:
- blockquote [ref=e109]: We have lobbied for this kind of clarity for the better part of a decade.
- paragraph [ref=e111]:
- text: 按一位资深内阁人士的说法,这份评估
- emphasis [ref=e112]: 是一个时代以来最连贯的战略蓝图
- text: ,但能否落地仍取决于春季立法。
- heading "Byline / Dateline / Caption" [level=2] [ref=e113]
- generic [ref=e114]:
- heading "元信息组件" [level=3] [ref=e116]
- generic [ref=e118]:
- generic [ref=e119]:
- generic [ref=e120]: BY ALICE SMITH
- text: LONDON —
- figure "A view of the Treasury terrace at dusk; the new commission will report here from May. Photograph by Jane Doe" [ref=e121]:
- generic [ref=e123]:
- text: A view of the Treasury terrace at dusk; the new commission will report here from May.
- generic [ref=e124]: Photograph by Jane Doe
- heading "视觉权重映射表" [level=2] [ref=e125]
- paragraph [ref=e127]:
- text: 下表从
- code [ref=e128]: visualWeights
- text: 数据动态生成,反映组件实际渲染时所用的 token。修改 theme 后表格自动同步。
- table [ref=e130]:
- rowgroup [ref=e131]:
- row "组件 权重 字体 字号 字重 行高 颜色" [ref=e132]:
- columnheader "组件" [ref=e133]
- columnheader "权重" [ref=e134]
- columnheader "字体" [ref=e135]
- columnheader "字号" [ref=e136]
- columnheader "字重" [ref=e137]
- columnheader "行高" [ref=e138]
- columnheader "颜色" [ref=e139]
- rowgroup [ref=e140]:
- row "Masthead Standard --font-family-masthead 56px 700 1 --nui-text-primary" [ref=e141]:
- cell "Masthead" [ref=e142]
- cell "Standard" [ref=e143]
- cell "--font-family-masthead" [ref=e144]:
- code [ref=e145]: "--font-family-masthead"
- cell "56px" [ref=e146]
- cell "700" [ref=e147]
- cell "1" [ref=e148]
- cell "--nui-text-primary" [ref=e149]:
- code [ref=e150]: "--nui-text-primary"
- row "Headline High --font-family-display 48px 600 1.05 --nui-text-primary" [ref=e151]:
- cell "Headline" [ref=e152]
- cell "High" [ref=e153]
- cell "--font-family-display" [ref=e154]:
- code [ref=e155]: "--font-family-display"
- cell "48px" [ref=e156]
- cell "600" [ref=e157]
- cell "1.05" [ref=e158]
- cell "--nui-text-primary" [ref=e159]:
- code [ref=e160]: "--nui-text-primary"
- row "Headline Medium --font-family-headline 32px 600 1.1 --nui-text-primary" [ref=e161]:
- cell "Headline" [ref=e162]
- cell "Medium" [ref=e163]
- cell "--font-family-headline" [ref=e164]:
- code [ref=e165]: "--font-family-headline"
- cell "32px" [ref=e166]
- cell "600" [ref=e167]
- cell "1.1" [ref=e168]
- cell "--nui-text-primary" [ref=e169]:
- code [ref=e170]: "--nui-text-primary"
- row "Headline Low --font-family-headline 22px 500 1.2 --nui-text-body" [ref=e171]:
- cell "Headline" [ref=e172]
- cell "Low" [ref=e173]
- cell "--font-family-headline" [ref=e174]:
- code [ref=e175]: "--font-family-headline"
- cell "22px" [ref=e176]
- cell "500" [ref=e177]
- cell "1.2" [ref=e178]
- cell "--nui-text-body" [ref=e179]:
- code [ref=e180]: "--nui-text-body"
- row "Subhead High --font-family-headline 18px 500 1.3 --nui-text-secondary" [ref=e181]:
- cell "Subhead" [ref=e182]
- cell "High" [ref=e183]
- cell "--font-family-headline" [ref=e184]:
- code [ref=e185]: "--font-family-headline"
- cell "18px" [ref=e186]
- cell "500" [ref=e187]
- cell "1.3" [ref=e188]
- cell "--nui-text-secondary" [ref=e189]:
- code [ref=e190]: "--nui-text-secondary"
- row "Subhead Medium --font-family-headline 16px 400 1.35 --nui-text-secondary" [ref=e191]:
- cell "Subhead" [ref=e192]
- cell "Medium" [ref=e193]
- cell "--font-family-headline" [ref=e194]:
- code [ref=e195]: "--font-family-headline"
- cell "16px" [ref=e196]
- cell "400" [ref=e197]
- cell "1.35" [ref=e198]
- cell "--nui-text-secondary" [ref=e199]:
- code [ref=e200]: "--nui-text-secondary"
- row "Kicker Standard --font-family-meta 12px 600 1.2 --nui-accent-primary" [ref=e201]:
- cell "Kicker" [ref=e202]
- cell "Standard" [ref=e203]
- cell "--font-family-meta" [ref=e204]:
- code [ref=e205]: "--font-family-meta"
- cell "12px" [ref=e206]
- cell "600" [ref=e207]
- cell "1.2" [ref=e208]
- cell "--nui-accent-primary" [ref=e209]:
- code [ref=e210]: "--nui-accent-primary"
- row "BodyText High --font-family-body 16px 400 1.6 --nui-text-body" [ref=e211]:
- cell "BodyText" [ref=e212]
- cell "High" [ref=e213]
- cell "--font-family-body" [ref=e214]:
- code [ref=e215]: "--font-family-body"
- cell "16px" [ref=e216]
- cell "400" [ref=e217]
- cell "1.6" [ref=e218]
- cell "--nui-text-body" [ref=e219]:
- code [ref=e220]: "--nui-text-body"
- row "BodyText Medium --font-family-body 15px 400 1.55 --nui-text-body" [ref=e221]:
- cell "BodyText" [ref=e222]
- cell "Medium" [ref=e223]
- cell "--font-family-body" [ref=e224]:
- code [ref=e225]: "--font-family-body"
- cell "15px" [ref=e226]
- cell "400" [ref=e227]
- cell "1.55" [ref=e228]
- cell "--nui-text-body" [ref=e229]:
- code [ref=e230]: "--nui-text-body"
- row "BodyText Low --font-family-body 13px 400 1.5 --nui-text-secondary" [ref=e231]:
- cell "BodyText" [ref=e232]
- cell "Low" [ref=e233]
- cell "--font-family-body" [ref=e234]:
- code [ref=e235]: "--font-family-body"
- cell "13px" [ref=e236]
- cell "400" [ref=e237]
- cell "1.5" [ref=e238]
- cell "--nui-text-secondary" [ref=e239]:
- code [ref=e240]: "--nui-text-secondary"
- row "Quote High --font-family-body 17px 400 1.55 --nui-text-quote" [ref=e241]:
- cell "Quote" [ref=e242]
- cell "High" [ref=e243]
- cell "--font-family-body" [ref=e244]:
- code [ref=e245]: "--font-family-body"
- cell "17px" [ref=e246]
- cell "400" [ref=e247]
- cell "1.55" [ref=e248]
- cell "--nui-text-quote" [ref=e249]:
- code [ref=e250]: "--nui-text-quote"
- row "Quote Medium --font-family-body 15px 400 1.5 --nui-text-quote" [ref=e251]:
- cell "Quote" [ref=e252]
- cell "Medium" [ref=e253]
- cell "--font-family-body" [ref=e254]:
- code [ref=e255]: "--font-family-body"
- cell "15px" [ref=e256]
- cell "400" [ref=e257]
- cell "1.5" [ref=e258]
- cell "--nui-text-quote" [ref=e259]:
- code [ref=e260]: "--nui-text-quote"
- row "PullQuote High --font-family-display 26px 600 1.2 --nui-text-primary" [ref=e261]:
- cell "PullQuote" [ref=e262]
- cell "High" [ref=e263]
- cell "--font-family-display" [ref=e264]:
- code [ref=e265]: "--font-family-display"
- cell "26px" [ref=e266]
- cell "600" [ref=e267]
- cell "1.2" [ref=e268]
- cell "--nui-text-primary" [ref=e269]:
- code [ref=e270]: "--nui-text-primary"
- row "PullQuote Medium --font-family-display 20px 500 1.25 --nui-text-body" [ref=e271]:
- cell "PullQuote" [ref=e272]
- cell "Medium" [ref=e273]
- cell "--font-family-display" [ref=e274]:
- code [ref=e275]: "--font-family-display"
- cell "20px" [ref=e276]
- cell "500" [ref=e277]
- cell "1.25" [ref=e278]
- cell "--nui-text-body" [ref=e279]:
- code [ref=e280]: "--nui-text-body"
- row "Byline Standard --font-family-meta 12px 500 1.3 --nui-text-secondary" [ref=e281]:
- cell "Byline" [ref=e282]
- cell "Standard" [ref=e283]
- cell "--font-family-meta" [ref=e284]:
- code [ref=e285]: "--font-family-meta"
- cell "12px" [ref=e286]
- cell "500" [ref=e287]
- cell "1.3" [ref=e288]
- cell "--nui-text-secondary" [ref=e289]:
- code [ref=e290]: "--nui-text-secondary"
- row "Dateline Standard --font-family-meta 12px 600 1.3 --nui-text-primary" [ref=e291]:
- cell "Dateline" [ref=e292]
- cell "Standard" [ref=e293]
- cell "--font-family-meta" [ref=e294]:
- code [ref=e295]: "--font-family-meta"
- cell "12px" [ref=e296]
- cell "600" [ref=e297]
- cell "1.3" [ref=e298]
- cell "--nui-text-primary" [ref=e299]:
- code [ref=e300]: "--nui-text-primary"
- row "Caption Standard --font-family-body 13px 400 1.4 --nui-text-secondary" [ref=e301]:
- cell "Caption" [ref=e302]
- cell "Standard" [ref=e303]
- cell "--font-family-body" [ref=e304]:
- code [ref=e305]: "--font-family-body"
- cell "13px" [ref=e306]
- cell "400" [ref=e307]
- cell "1.4" [ref=e308]
- cell "--nui-text-secondary" [ref=e309]:
- code [ref=e310]: "--nui-text-secondary"
@@ -0,0 +1,121 @@
- generic [ref=e2]:
- complementary [ref=e3]:
- link "NewspaperUI" [ref=e4] [cursor=pointer]:
- /url: /
- generic [ref=e5]: Production Newspaper Components
- navigation [ref=e6]:
- list [ref=e7]:
- listitem [ref=e8]:
- link "概览 / 头版" [ref=e9] [cursor=pointer]:
- /url: /
- listitem [ref=e10]:
- link "栅格系统" [ref=e11] [cursor=pointer]:
- /url: /grid-system
- listitem [ref=e12]:
- link "布局组件" [ref=e13] [cursor=pointer]:
- /url: /components/article
- list [ref=e14]:
- listitem [ref=e15]:
- link "Masthead" [ref=e16] [cursor=pointer]:
- /url: /components/masthead
- listitem [ref=e17]:
- link "Article + Layer" [ref=e18] [cursor=pointer]:
- /url: /components/article
- listitem [ref=e19]:
- link "Rule 分隔线" [ref=e20] [cursor=pointer]:
- /url: /components/rule
- listitem [ref=e21]:
- link "文本组件" [ref=e22] [cursor=pointer]:
- /url: /text
- listitem [ref=e23]:
- link "媒体组件" [ref=e24] [cursor=pointer]:
- /url: /components/media
- listitem [ref=e25]:
- link "主题与颜色" [ref=e26] [cursor=pointer]:
- /url: /theme
- listitem [ref=e27]:
- link "示例" [ref=e28] [cursor=pointer]:
- /url: /examples/spanning
- list [ref=e29]:
- listitem [ref=e30]:
- link "跨栏布局" [ref=e31] [cursor=pointer]:
- /url: /examples/spanning
- listitem [ref=e32]:
- link "响应式" [ref=e33] [cursor=pointer]:
- /url: /examples/responsive
- listitem [ref=e34]:
- link "Blackletter 头版" [ref=e35] [cursor=pointer]:
- /url: /examples/blackletter-frontpage
- main [ref=e36]:
- article [ref=e39]:
- heading "主题与颜色" [level=1] [ref=e40]
- paragraph [ref=e41]: 设计哲学:暖灰系 + warm off-white。不使用纯黑(#000)也不使用纯白(#FFF), 把屏幕配色带回纸面的温度。
- heading "主题切换" [level=2] [ref=e42]
- paragraph [ref=e44]: 点击切换深色模式。深色基调采用暖深棕黑(#14110D)而非冷蓝黑,保持报纸的纸性触感。
- button "Dark Mode" [ref=e46] [cursor=pointer]
- heading "文字色 token" [level=2] [ref=e47]
- generic [ref=e48]:
- generic [ref=e51]:
- code [ref=e52]: "--nui-text-primary"
- generic [ref=e53]: "#1A1A1A · 标题、主文本"
- generic [ref=e56]:
- code [ref=e57]: "--nui-text-body"
- generic [ref=e58]: "#22201C · 正文"
- generic [ref=e61]:
- code [ref=e62]: "--nui-text-secondary"
- generic [ref=e63]: "#4A4742 · Subhead、次级"
- generic [ref=e66]:
- code [ref=e67]: "--nui-text-muted"
- generic [ref=e68]: "#6E6A63 · Caption、注释"
- generic [ref=e71]:
- code [ref=e72]: "--nui-text-quote"
- generic [ref=e73]: "#2E2A24 · Quote 主体"
- heading "背景与分隔线 token" [level=2] [ref=e74]
- generic [ref=e75]:
- generic [ref=e78]:
- code [ref=e79]: "--nui-bg-page"
- generic [ref=e80]: "#F7F4ED · Warm off-white 页面底"
- generic [ref=e83]:
- code [ref=e84]: "--nui-bg-surface"
- generic [ref=e85]: "#FBF9F4 · 次级面板背景"
- generic [ref=e88]:
- code [ref=e89]: "--nui-rule-hairline"
- generic [ref=e90]: "#C9C2B2 · 细线分隔"
- generic [ref=e93]:
- code [ref=e94]: "--nui-rule-decorative"
- generic [ref=e95]: "#1A1A1A · 强调线"
- generic [ref=e98]:
- code [ref=e99]: "--nui-highlight"
- generic [ref=e100]: "#F2E9C8 · 旧报纸黄"
- heading "强调色 token" [level=2] [ref=e101]
- generic [ref=e102]:
- generic [ref=e105]:
- code [ref=e106]: "--nui-accent-primary"
- generic [ref=e107]: "#7A1F1F · Brick red, Kicker / Masthead 强调"
- generic [ref=e110]:
- code [ref=e111]: "--nui-accent-ink-blue"
- generic [ref=e112]: "#1B2A4A · The Times 蓝"
- generic [ref=e115]:
- code [ref=e116]: "--nui-highlight"
- generic [ref=e117]: "#F2E9C8 · 高亮底色"
- heading "字体家族" [level=2] [ref=e118]
- paragraph [ref=e120]: 全部经典严肃风字体:Cormorant Garamond 承担报头与展示,Source Serif 4 贯穿正文与标题,Inter 处理 small-caps 元信息。Blackletter preset 通过 UnifrakturMaguntia 切入哥特报头。
- generic [ref=e121]:
- generic [ref=e122]:
- generic [ref=e123]: "--font-family-masthead · Cormorant Garamond — 报头"
- generic [ref=e124]: The Daily Chronicle
- generic [ref=e125]:
- generic [ref=e126]: "--font-family-blackletter · UnifrakturMaguntia — Blackletter preset"
- generic [ref=e127]: The Daily Chronicle
- generic [ref=e128]:
- generic [ref=e129]: "--font-family-display · Source Serif 4 — Display 大字头条"
- generic [ref=e130]: A Quiet Revolution
- generic [ref=e131]:
- generic [ref=e132]: "--font-family-headline · Source Serif 4 — Headline / Subhead"
- generic [ref=e133]: Whitehall confirms review
- generic [ref=e134]:
- generic [ref=e135]: "--font-family-body · Source Serif 4 — 正文"
- generic [ref=e136]: In Manchester, regional officials greeted the announcement.
- generic [ref=e137]:
- generic [ref=e138]: "--font-family-meta · Inter — small-caps 元信息"
- generic [ref=e139]: BY ALICE SMITH · LONDON
+240
View File
@@ -0,0 +1,240 @@
# Skill: Agent-Driven 从 0 到 1 落地复杂前端项目
## 适用场景
- 从设计文档(design.md / PRD)出发,构建完整的组件库 / 应用
- 项目涉及多个 package / 多层架构(theme → utils → components → docs
- 需要达到「生产级」视觉和代码质量
- 单人操作但希望并行推进多个模块
---
## 核心工作流(5 阶段)
```
需求分析 → 技术规划 → 并行实施 → 视觉验证 → 设计复评
↑ |
└──── 如果复评 < 目标分 → 回到规划 ────────────┘
```
---
## 阶段 1:需求分析(不写代码)
### 1.1 读蓝本,提炼决策点
读 design.md,列出所有需要用户决策的方向性问题(不超过 4 个)。用 `AskUserQuestion` 一次性收集,避免来回。
**关键决策点模板**
- 视觉风格基调?(给 2-3 个带 ASCII preview 的选项)
- 核心技术机制?(给 2-3 个方案对比)
- 交付物范围?(MVP vs 完整版)
- 现有代码处理?(保留 / 重写 / 增量)
### 1.2 派发探索 Agent(只读)
并行派 2 个 Explore agent
- Agent A:探索现有代码结构、工具链版本、可复用的好东西
- Agent B:做领域调研(视觉系统 / 技术方案 / 竞品参考)
**产出**:结构化报告(缺口矩阵 + 可复用清单 + 调研结论)
---
## 阶段 2:技术规划(Plan Mode
### 2.1 进入 Plan Mode
`EnterPlanMode`,基于阶段 1 的探索结果写 plan 文件。
### 2.2 Plan 文件结构
```markdown
# 项目名 重做/新建 计划
## Context(为什么做、做什么、预期结果)
## 范围与边界(保留什么 / 重写什么 / 不做什么)
## 1-N. 各 Package 详细设计
- 文件清单
- 关键代码模板(不是伪代码,是可直接落地的 TSX)
- 依赖关系
## 实施顺序(Stage 1 → N,标注串行/并行)
## 验证方案(自动化 + 视觉清单)
## 风险与权衡
```
### 2.3 关键原则
- **Plan 里写真实代码模板**,不写「大概这样做」。Agent 执行时直接复制粘贴,减少发挥空间
- **标注依赖关系**:哪些 Stage 可并行、哪些必须串行
- **验证清单要具体**:不写「检查样式是否正确」,写「Headline fontSize ≥ 48px, fontWeight === 600」
---
## 阶段 3:并行实施
### 3.1 任务拆分
`TaskCreate` 按 Stage 建任务,用 `addBlockedBy` 标注依赖:
```
Stage 1 (基础设施) ← 无依赖,先做
Stage 2 (组件层) ← blocked by Stage 1
Stage 3 (Demo) ← blocked by Stage 2
Stage 4 (文档) ← blocked by Stage 2(可与 Stage 3 并行)
Stage 5 (验证) ← blocked by Stage 3 + 4
```
### 3.2 Agent 派发策略
| 场景 | 做法 |
|---|---|
| 有依赖的 Stage | 串行:等前一个 Agent 完成再派下一个 |
| 无依赖的 Stage | 并行:同一条消息里派多个 Agent |
| Agent 需要协调 | 用 `SendMessage` 通知另一个 Agent 上游已就绪 |
### 3.3 Agent Prompt 黄金模板
```
你需要在 [路径] 实现 [什么]。
## 上游依赖(已就绪)
[列出可 import 的模块和 API]
## 任务
### 1. [具体文件] — [完整代码]
### 2. [具体文件] — [完整代码]
...
## 验证步骤
[具体命令 + 期望输出]
## 约束
- 严格按上面代码写,不要发挥
- 不引入新依赖
- 完成后直接报告,不要询问
```
**关键**:给 Agent 的 prompt 越具体、代码越完整,产出质量越高。不要给 Agent 留设计决策空间。
### 3.4 每个 Stage 完成后
-`pnpm build` + `pnpm test` 确认不破坏
- 标记 TaskUpdate status=completed
- 解锁下游 Stage
---
## 阶段 4:视觉验证
### 4.1 自动化验证
```bash
pnpm build # 全包构建
pnpm test # 全测试通过
pnpm dev # 启动 dev server
```
### 4.2 Playwright DOM 实测
不依赖截图肉眼判断,用 `browser_evaluate` 直接读 computed style
```javascript
// 验证字体、字号、颜色、布局属性
const cs = getComputedStyle(element);
return {
fontFamily: cs.fontFamily,
fontSize: cs.fontSize,
fontWeight: cs.fontWeight,
columnCount: cs.columnCount,
backgroundColor: cs.backgroundColor,
};
```
### 4.3 视觉验收清单
提前在 Plan 里写好 10-15 项具体的验收条件,每项都是可自动化检测的:
```
- [ ] Masthead fontFamily 包含 "Cormorant Garamond"
- [ ] Headline fontSize >= 48px
- [ ] BodyText columnCount === "3"
- [ ] body backgroundColor === "rgb(247, 244, 237)"
```
---
## 阶段 5:设计复评
### 5.1 派发 UI Designer Agent
给 Designer Agent
- 截图路径
- 关键文件路径
- 第一次评估结果(如果是迭代)
- 明确的评分维度和输出格式
### 5.2 目标分数
- 首次实现目标:≥ 8/10
- 迭代后目标:≥ 9/10
- 如果 < 目标分,回到阶段 2 重新规划问题区域
---
## 反模式(避免)
| 反模式 | 正确做法 |
|---|---|
| 给 Agent 模糊指令「实现组件库」 | 给完整代码模板 + 文件路径 |
| 一个 Agent 做所有事 | 按 Stage 拆分,每个 Agent 职责单一 |
| 先写代码再想设计 | Plan Mode 先对齐,用户批准再动手 |
| 只跑 build 就算完成 | Playwright 实测 computed style |
| 截图肉眼看「差不多」 | 量化验收清单(字号/颜色/布局属性) |
| 一次性全部并行 | 按依赖关系分层,基础设施必须先完成 |
| Agent 报错就放弃 | 分析根因,调整 prompt 重试(最多 3 次) |
---
## 时间分配参考
| 阶段 | 占比 | 说明 |
|---|---|---|
| 需求分析 + 探索 | 15% | 磨刀不误砍柴工 |
| 技术规划 | 20% | Plan 越详细,实施越快 |
| 并行实施 | 45% | Agent 并行,人只做协调 |
| 验证 + 复评 | 15% | 量化验收,不靠感觉 |
| 迭代修复 | 5% | 如果规划到位,这步很轻 |
---
## 可复用的 Agent 类型
| Agent 类型 | 用途 |
|---|---|
| Explore | 只读探索代码结构、生成报告 |
| UI Designer | 视觉系统调研、设计复评打分 |
| Frontend Developer | 写代码、跑构建、跑测试 |
| Minimal Change Engineer | 精准修复特定问题,不扩大范围 |
---
## Checklist:启动新项目时
1. [ ] 有 design.md / PRD 蓝本?
2. [ ] 用 AskUserQuestion 收集 3-4 个方向性决策?
3. [ ] 派 Explore agent 做现状分析 + 领域调研?
4. [ ] 进入 Plan Mode 写详细计划(含代码模板)?
5. [ ] 用户批准 plan
6. [ ] 按依赖关系拆 TaskCreate + addBlockedBy
7. [ ] Stage 1 基础设施先串行完成?
8. [ ] Stage 2+ 尽量并行派发?
9. [ ] 每个 Stage 完成后跑 build + test
10. [ ] Playwright 量化验收 10+ 项?
11. [ ] Designer Agent 复评 ≥ 目标分?
12. [ ] 同步 design.md / README
@@ -0,0 +1,32 @@
'use client';
import React, { ReactNode, CSSProperties } from 'react';
import { clampSpan, cx } from '@newspaperui/utils';
import { useSection } from './Section';
export interface ArticleProps {
span?: number;
breakable?: boolean;
className?: string;
style?: CSSProperties;
children: ReactNode;
}
export const Article: React.FC<ArticleProps> = ({
span, breakable = true, className, style, children,
}) => {
const section = useSection();
const cols = span ? clampSpan(span, section.columns) : section.columns;
return (
<article
className={cx('nui-article', className)}
style={{
gridColumn: `span ${cols}`,
breakInside: breakable ? 'auto' : 'avoid',
...style,
}}
data-span={cols}
>
{children}
</article>
);
};
+26
View File
@@ -0,0 +1,26 @@
'use client';
import React, { ReactNode, CSSProperties } from 'react';
export interface LayerProps {
position?: 'absolute' | 'fixed' | 'sticky';
top?: string | number;
left?: string | number;
right?: string | number;
bottom?: string | number;
zIndex?: number;
className?: string;
style?: CSSProperties;
children: ReactNode;
}
export const Layer: React.FC<LayerProps> = ({
position = 'absolute', top, left, right, bottom, zIndex,
className, style, children,
}) => (
<div
className={className}
style={{ position, top, left, right, bottom, zIndex, ...style }}
>
{children}
</div>
);
@@ -0,0 +1,81 @@
'use client';
import React from 'react';
import { cx } from '@newspaperui/utils';
export interface MastheadProps {
title: string;
kicker?: string;
edition?: string;
date?: string;
price?: string;
variant?: 'classic' | 'blackletter' | 'modern';
className?: string;
}
export const Masthead: React.FC<MastheadProps> = ({
title, kicker, edition, date, price, variant = 'classic', className,
}) => {
const fontFamily =
variant === 'blackletter'
? 'var(--font-family-blackletter)'
: 'var(--font-family-masthead)';
const align = variant === 'modern' ? 'left' : 'center';
return (
<header
className={cx('nui-masthead', `nui-masthead--${variant}`, className)}
style={{
gridColumn: '1 / -1',
textAlign: align,
color: 'var(--nui-text-primary)',
}}
>
<div className="nui-masthead-rule-top" />
{kicker && (
<div
className="nui-small-caps"
style={{
fontSize: '12px',
color: 'var(--nui-accent-primary)',
margin: 'var(--nui-space-2) 0',
letterSpacing: '0.1em',
}}
>
{kicker}
</div>
)}
<h1
style={{
fontFamily,
fontWeight: 700,
fontSize: variant === 'modern' ? 'clamp(40px, 6vw, 72px)' : 'clamp(48px, 8vw, 96px)',
lineHeight: 1.0,
letterSpacing: variant === 'blackletter' ? '0' : '0.02em',
margin: 'var(--nui-space-2) 0 var(--nui-space-3) 0',
}}
>
{title}
</h1>
{(edition || date || price) && (
<div
className="nui-small-caps"
style={{
display: 'flex',
justifyContent: align === 'left' ? 'flex-start' : 'space-between',
gap: 'var(--nui-space-6)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
padding: 'var(--nui-space-2) 0',
margin: 0,
letterSpacing: '0.08em',
}}
>
{edition && <span>{edition}</span>}
{date && <span>{date}</span>}
{price && <span>{price}</span>}
</div>
)}
<div className="nui-masthead-rule-bottom" />
</header>
);
};
+56
View File
@@ -0,0 +1,56 @@
'use client';
import React, { CSSProperties } from 'react';
import { cx } from '@newspaperui/utils';
export interface RuleProps {
variant?: 'hairline' | 'double' | 'thick';
orientation?: 'horizontal' | 'vertical';
span?: number; // 横向时占多少列 (1 / -1 全跨)
className?: string;
style?: CSSProperties;
}
export const Rule: React.FC<RuleProps> = ({
variant = 'hairline', orientation = 'horizontal', span, className, style,
}) => {
const isHorizontal = orientation === 'horizontal';
const baseStyle: CSSProperties = isHorizontal
? { width: '100%', border: 0, margin: 0 }
: { height: '100%', width: '1px', border: 0, margin: 0 };
const variantStyle: CSSProperties = (() => {
if (variant === 'hairline')
return isHorizontal
? { borderTop: '1px solid var(--nui-rule-hairline)' }
: { background: 'var(--nui-rule-hairline)' };
if (variant === 'thick')
return isHorizontal
? { borderTop: '3px solid var(--nui-rule-decorative)' }
: { width: '3px', background: 'var(--nui-rule-decorative)' };
// double
return isHorizontal
? {
height: '6px',
background:
'linear-gradient(to bottom, var(--nui-rule-decorative) 0 1px, transparent 1px 4px, var(--nui-rule-decorative) 4px 6px)',
}
: {
width: '6px',
background:
'linear-gradient(to right, var(--nui-rule-decorative) 0 1px, transparent 1px 4px, var(--nui-rule-decorative) 4px 6px)',
};
})();
return (
<hr
className={cx('nui-rule', `nui-rule--${variant}`, className)}
style={{
...baseStyle,
...variantStyle,
gridColumn: isHorizontal && span ? `span ${span}` : isHorizontal ? '1 / -1' : undefined,
...style,
}}
aria-orientation={orientation}
/>
);
};
@@ -0,0 +1,53 @@
'use client';
import React, { createContext, useContext, ReactNode, CSSProperties } from 'react';
import { clampSpan, cx } from '@newspaperui/utils';
import { useLayout } from './Layout';
export interface SectionProps {
columns: number; // section 内部的栅格列数 (≤ layout.columns)
gap?: string; // 默认 'var(--nui-gutter)'
breakable?: boolean; // 是否允许 print 分页断开,默认 true
divider?: 'none' | 'top' | 'bottom' | 'both'; // hairline 分隔
className?: string;
style?: CSSProperties;
children: ReactNode;
}
interface SectionContextValue { columns: number; }
const SectionContext = createContext<SectionContextValue>({ columns: 24 });
export const useSection = () => useContext(SectionContext);
export const Section: React.FC<SectionProps> = ({
columns, gap = 'var(--nui-gutter)', breakable = true, divider = 'none',
className, style, children,
}) => {
const layout = useLayout();
const cols = clampSpan(columns, layout.columns);
const dividerStyle: CSSProperties = {};
if (divider === 'top' || divider === 'both')
dividerStyle.borderTop = '1px solid var(--nui-rule-hairline)';
if (divider === 'bottom' || divider === 'both')
dividerStyle.borderBottom = '1px solid var(--nui-rule-hairline)';
return (
<SectionContext.Provider value={{ columns: cols }}>
<section
className={cx('nui-section', className)}
style={{
display: 'grid',
gridTemplateColumns: `repeat(${cols}, 1fr)`,
gap,
breakInside: breakable ? 'auto' : 'avoid',
...dividerStyle,
paddingTop: divider === 'top' || divider === 'both' ? 'var(--nui-space-4)' : undefined,
paddingBottom: divider === 'bottom' || divider === 'both' ? 'var(--nui-space-4)' : undefined,
...style,
}}
data-columns={cols}
>
{children}
</section>
</SectionContext.Provider>
);
};
+31
View File
@@ -0,0 +1,31 @@
'use client';
import React, { ReactNode, CSSProperties } from 'react';
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
import { cx } from '@newspaperui/utils';
export interface DatelineProps {
className?: string;
style?: CSSProperties;
children: ReactNode; // e.g. "LONDON —"
}
export const Dateline: React.FC<DatelineProps> = ({ className, style, children }) => {
const config = visualWeights.Dateline.Standard!;
return (
<span
className={cx('nui-dateline nui-small-caps', className)}
style={{
fontFamily: `var(${config.fontFamily})`,
fontSize: resolveFontSize(config.fontSize),
fontWeight: config.fontWeight,
lineHeight: config.lineHeight,
letterSpacing: config.letterSpacing,
color: `var(${config.color})`,
margin: config.margin,
...style,
}}
>
{children}
</span>
);
};
+31
View File
@@ -0,0 +1,31 @@
'use client';
import React, { ReactNode, CSSProperties } from 'react';
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
import { cx } from '@newspaperui/utils';
export interface KickerProps {
className?: string;
style?: CSSProperties;
children: ReactNode;
}
export const Kicker: React.FC<KickerProps> = ({ className, style, children }) => {
const config = visualWeights.Kicker.Standard!;
return (
<div
className={cx('nui-kicker nui-small-caps', className)}
style={{
fontFamily: `var(${config.fontFamily})`,
fontSize: resolveFontSize(config.fontSize),
fontWeight: config.fontWeight,
lineHeight: config.lineHeight,
letterSpacing: config.letterSpacing,
color: `var(${config.color})`,
margin: config.margin,
...style,
}}
>
{children}
</div>
);
};
@@ -0,0 +1,53 @@
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { Layout } from '../layout/Layout';
import { Section } from '../layout/Section';
import { BodyText } from '../text/BodyText';
describe('BodyText', () => {
it('enables multi-column flow when columns >= 2', () => {
const { container } = render(
<Layout>
<Section columns={12}>
<BodyText columns={3}>
<p>p1</p>
</BodyText>
</Section>
</Layout>,
);
const root = container.querySelector('.nui-bodytext') as HTMLElement;
expect(root.style.columnCount).toBe('3');
expect(root.style.columnGap).toBe('var(--nui-gutter)');
expect(root.classList.contains('nui-column-rule')).toBe(true);
expect(root.getAttribute('data-columns')).toBe('3');
});
it('applies the nui-drop-cap class when dropCap is true', () => {
const { container } = render(
<Layout>
<Section columns={12}>
<BodyText dropCap>
<p>p</p>
</BodyText>
</Section>
</Layout>,
);
const root = container.querySelector('.nui-bodytext') as HTMLElement;
expect(root.classList.contains('nui-drop-cap')).toBe(true);
});
it('does not enable multi-column flow when columns is 1 (default)', () => {
const { container } = render(
<Layout>
<Section columns={12}>
<BodyText>
<p>p</p>
</BodyText>
</Section>
</Layout>,
);
const root = container.querySelector('.nui-bodytext') as HTMLElement;
expect(root.style.columnCount).toBe('');
expect(root.classList.contains('nui-column-rule')).toBe(false);
});
});
@@ -0,0 +1,53 @@
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { Layout } from '../layout/Layout';
import { Section } from '../layout/Section';
import { Headline } from '../text/Headline';
describe('Headline', () => {
it('renders h1 for weight="High" and h2 for weight="Medium"', () => {
const { container, rerender } = render(
<Layout>
<Section columns={12}>
<Headline weight="High">A</Headline>
</Section>
</Layout>,
);
expect(container.querySelector('h1')).not.toBeNull();
rerender(
<Layout>
<Section columns={12}>
<Headline weight="Medium">B</Headline>
</Section>
</Layout>,
);
expect(container.querySelector('h2')).not.toBeNull();
});
it('respects "as" prop overriding default tag', () => {
const { container } = render(
<Layout>
<Section columns={12}>
<Headline weight="High" as="h2">
X
</Headline>
</Section>
</Layout>,
);
expect(container.querySelector('h1')).toBeNull();
expect(container.querySelector('h2')).not.toBeNull();
});
it('uses CSS variable references for fontFamily', () => {
const { container } = render(
<Layout>
<Section columns={12}>
<Headline weight="High">X</Headline>
</Section>
</Layout>,
);
const h1 = container.querySelector('h1') as HTMLElement;
expect(h1.style.fontFamily).toContain('var(--font-family-');
});
});
@@ -0,0 +1,31 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { Masthead } from '../layout/Masthead';
describe('Masthead', () => {
it('renders title as an h1', () => {
render(<Masthead title="The Daily Times" />);
const h1 = screen.getByRole('heading', { level: 1 });
expect(h1.textContent).toBe('The Daily Times');
});
it('uses the blackletter font family when variant="blackletter"', () => {
render(<Masthead title="Gazette" variant="blackletter" />);
const h1 = screen.getByRole('heading', { level: 1 }) as HTMLElement;
expect(h1.style.fontFamily).toContain('--font-family-blackletter');
});
it('renders edition, date, and price when provided', () => {
render(
<Masthead
title="The Daily Times"
edition="Late Edition"
date="May 19, 2026"
price="$3.00"
/>,
);
expect(screen.getByText('Late Edition')).toBeInTheDocument();
expect(screen.getByText('May 19, 2026')).toBeInTheDocument();
expect(screen.getByText('$3.00')).toBeInTheDocument();
});
});
@@ -0,0 +1,219 @@
'use client';
import {
Layout,
Section,
Article,
Layer,
Headline,
Subhead,
BodyText,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
import { PropsTable } from '@/components/PropsTable';
export default function ArticlePage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
Article + Layer
</Headline>
<Subhead weight="Medium">
Article grid-column span Layer
</Subhead>
<Headline weight="Low" as="h2">
Article API
</Headline>
<PropsTable
data={[
{
name: 'span',
type: 'number',
description: '跨多少列(≤ Section.columns)。缺省时占满全宽。',
},
{
name: 'breakable',
type: 'boolean',
default: 'true',
description: '允许打印时跨页断开。',
},
{
name: 'className',
type: 'string',
description: '自定义 CSS 类名。',
},
{
name: 'style',
type: 'CSSProperties',
description: '自定义内联样式。',
},
{
name: 'children',
type: 'ReactNode',
required: true,
description: '文章内容。',
},
]}
/>
<Headline weight="Low" as="h2">
Demo
</Headline>
<Demo
title="8 + 16 跨栏"
description="左侧 8 列 + 右侧 16 列,模拟短讯 + 主稿布局。"
code={`<Layout columns={24}>
<Section columns={24}>
<Article span={8}>
<Headline weight="Low">短讯</Headline>
<BodyText>...</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium">主稿</Headline>
<BodyText columns={2}>...</BodyText>
</Article>
</Section>
</Layout>`}
>
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={8}>
<Headline weight="Low" as="h3">
</Headline>
<BodyText weight="Low">
<p>
Markets responded cautiously through the morning session. The pound
traded sideways against the dollar, gilts firmed by three basis points.
</p>
</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium" as="h3">
稿
</Headline>
<BodyText columns={2}>
<p>
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.
</p>
<p>
Critics inside the cabinet caution that the timing risks overshadowing
the chancellor&rsquo;s autumn statement, while supporters describe the
proposals as the most coherent strategic blueprint in a generation.
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
<Headline weight="Low" as="h2" style={{ marginTop: '2rem' }}>
Layer API
</Headline>
<BodyText weight="Low">
<p>
Layer 使 CSS position 广sticky
</p>
</BodyText>
<PropsTable
data={[
{
name: 'position',
type: "'absolute' | 'fixed' | 'sticky'",
default: "'absolute'",
description: 'CSS position 属性。',
},
{
name: 'top',
type: 'string | number',
description: '距离顶部的距离。',
},
{
name: 'left',
type: 'string | number',
description: '距离左侧的距离。',
},
{
name: 'right',
type: 'string | number',
description: '距离右侧的距离。',
},
{
name: 'bottom',
type: 'string | number',
description: '距离底部的距离。',
},
{
name: 'zIndex',
type: 'number',
description: 'z-index 层级。',
},
{
name: 'children',
type: 'ReactNode',
required: true,
description: '浮动层内容。',
},
]}
/>
<Demo
title="Layer 浮动拉引"
description="在文章旁边添加浮动的拉引文字。父容器需要 position: relative。"
code={`<div style={{ position: 'relative', minHeight: '200px' }}>
<Article span={16}>
<Headline weight="Low">主要文章</Headline>
<BodyText>...</BodyText>
</Article>
<Layer position="absolute" top="0" right="0">
<aside>浮动内容</aside>
</Layer>
</div>`}
>
<Layout columns={24} padding="0">
<Section columns={24}>
<div style={{ position: 'relative', minHeight: '200px', gridColumn: 'span 24' }}>
<Article span={16}>
<Headline weight="Low" as="h3">
</Headline>
<BodyText>
<p>
In Manchester, regional officials greeted the announcement with
measured optimism. The mayor&rsquo;s office is expected to issue a
formal response by week&rsquo;s end.
</p>
</BodyText>
</Article>
<Layer position="absolute" top="0" right="0">
<aside
style={{
width: '180px',
padding: '1rem',
borderTop: '1px solid var(--nui-rule-hairline)',
borderBottom: '1px solid var(--nui-rule-hairline)',
fontFamily: 'var(--font-family-display)',
fontSize: '18px',
lineHeight: 1.3,
color: 'var(--nui-text-primary)',
}}
>
&ldquo;We have lobbied for this kind of clarity for the better part of
a decade.&rdquo;
</aside>
</Layer>
</div>
</Section>
</Layout>
</Demo>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,148 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
Masthead,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
import { PropsTable } from '@/components/PropsTable';
export default function MastheadPage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
Masthead
</Headline>
<Subhead weight="Medium">
variant 线
</Subhead>
<Headline weight="Low" as="h2">
API
</Headline>
<PropsTable
data={[
{
name: 'title',
type: 'string',
required: true,
description: '报名文字。',
},
{
name: 'variant',
type: "'classic' | 'blackletter' | 'modern'",
default: "'classic'",
description: '视觉风格。',
},
{
name: 'kicker',
type: 'string',
description: '报名上方的小标(如 "All the News That\'s Fit to Print")。',
},
{
name: 'edition',
type: 'string',
description: '版次(如 "Vol. CLXXII No. 59,678")。',
},
{
name: 'date',
type: 'string',
description: '日期(如 "Monday, May 19, 2026")。',
},
{
name: 'price',
type: 'string',
description: '售价(如 "$3.00")。',
},
]}
/>
<Headline weight="Low" as="h2">
Classic
</Headline>
<Demo
title="classic variant"
description="双线居中,Cormorant Garamond 衬线。"
code={`<Masthead
variant="classic"
title="The Daily Chronicle"
kicker="All the News That's Fit to Print"
edition="Vol. CLXXII No. 59,678"
date="Monday, May 19, 2026"
price="$3.00"
/>`}
>
<Masthead
variant="classic"
title="The Daily Chronicle"
kicker="All the News That's Fit to Print"
edition="Vol. CLXXII No. 59,678"
date="Monday, May 19, 2026"
price="$3.00"
/>
</Demo>
<Headline weight="Low" as="h2">
Blackletter
</Headline>
<Demo
title="blackletter variant"
description="UnifrakturMaguntia 哥特体,The Times 风格。"
code={`<Masthead
variant="blackletter"
title="The Evening Standard"
edition="Late Final Edition"
date="Monday, May 19, 2026"
price="£2.50"
/>`}
>
<Masthead
variant="blackletter"
title="The Evening Standard"
edition="Late Final Edition"
date="Monday, May 19, 2026"
price="£2.50"
/>
</Demo>
<Headline weight="Low" as="h2">
Modern
</Headline>
<Demo
title="modern variant"
description="左对齐 + accent 强调色,适合数字优先的出版物。"
code={`<Masthead
variant="modern"
title="The Observer"
kicker="Sunday Edition"
date="May 19, 2026"
/>`}
>
<Masthead
variant="modern"
title="The Observer"
kicker="Sunday Edition"
date="May 19, 2026"
/>
</Demo>
<Headline weight="Low" as="h2">
使
</Headline>
<BodyText weight="Low">
<p>
Masthead <code>gridColumn: 1 / -1</code> Section
Layout Section Rule 线
</p>
</BodyText>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,166 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
Figure,
PullQuote,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
import { PropsTable } from '@/components/PropsTable';
export default function MediaPage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
ImageFigureVideoPullQuote
</Subhead>
<Headline weight="Low" as="h2">
Image
</Headline>
<BodyText weight="Low">
<p>
<code>display: block; width: 100%</code> Section
span
</p>
</BodyText>
<PropsTable
data={[
{ name: 'src', type: 'string', required: true, description: '图片 URL。' },
{ name: 'alt', type: 'string', required: true, description: '替代文本。' },
{ name: 'span', type: 'number', description: '跨栏数。' },
]}
/>
<Headline weight="Low" as="h2">
Figure
</Headline>
<BodyText weight="Low">
<p>
Image + Caption + Credit figcaption
</p>
</BodyText>
<PropsTable
data={[
{ name: 'src', type: 'string', required: true, description: '图片 URL。' },
{ name: 'alt', type: 'string', required: true, description: '替代文本。' },
{ name: 'caption', type: 'string', description: '图片说明。' },
{ name: 'credit', type: 'string', description: '摄影师/来源(small-caps 渲染)。' },
{ name: 'span', type: 'number', description: '跨栏数。' },
]}
/>
<Demo
title="Figure 带 caption 和 credit"
description="使用 Unsplash 占位图演示完整效果。"
code={`<Figure
src="https://images.unsplash.com/photo-1495020689067-958852a7765e?w=800&h=400&fit=crop"
alt="Newspapers on a wooden table"
caption="A view of the morning editions, arranged on the newsroom table."
credit="Photograph by Roman Kraft / Unsplash"
/>`}
>
<Figure
src="https://images.unsplash.com/photo-1495020689067-958852a7765e?w=800&h=400&fit=crop"
alt="Newspapers on a wooden table"
caption="A view of the morning editions, arranged on the newsroom table."
credit="Photograph by Roman Kraft / Unsplash"
/>
</Demo>
<Headline weight="Low" as="h2">
Video
</Headline>
<PropsTable
data={[
{ name: 'src', type: 'string', required: true, description: '视频 URL。' },
{ name: 'poster', type: 'string', description: '封面图 URL。' },
{ name: 'caption', type: 'string', description: '视频说明。' },
{ name: 'credit', type: 'string', description: '来源。' },
{ name: 'controls', type: 'boolean', default: 'true', description: '显示播放控件。' },
{ name: 'span', type: 'number', description: '跨栏数。' },
]}
/>
<BodyText weight="Low">
<p>
Video Figure <code>&lt;video&gt;</code>
caption/credit
</p>
</BodyText>
<Headline weight="Low" as="h2">
PullQuote
</Headline>
<BodyText weight="Low">
<p>
hairline + Display BodyText
<code>spanAllColumns</code> <code>column-span: all</code>
</p>
</BodyText>
<PropsTable
data={[
{ name: 'weight', type: "'High' | 'Medium'", default: "'High'", description: '视觉权重。' },
{ name: 'span', type: 'number', description: '在 Section 栅格内跨栏。' },
{ name: 'spanAllColumns', type: 'boolean', default: 'false', description: '在多栏 BodyText 内跨所有栏。' },
{ name: 'author', type: 'string', description: '引用来源。' },
{ name: 'align', type: "'left' | 'center'", default: "'left'", description: '文本对齐。' },
]}
/>
<Demo
title="PullQuote 独立使用"
code={`<PullQuote author="Senior Cabinet Official">
We have lobbied for this kind of clarity for the better part of a decade.
</PullQuote>`}
>
<PullQuote author="Senior Cabinet Official">
We have lobbied for this kind of clarity for the better part of a decade.
</PullQuote>
</Demo>
<Demo
title="PullQuote 在多栏 BodyText 内跨栏"
description="spanAllColumns 使 PullQuote 通过 column-span: all 跨越所有文字栏。"
code={`<BodyText columns={3}>
<p>...</p>
<PullQuote spanAllColumns author="...">...</PullQuote>
<p>...</p>
</BodyText>`}
>
<BodyText columns={3}>
<p>
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.
</p>
<PullQuote spanAllColumns author="Senior Cabinet Official">
The most coherent strategic blueprint in a generation.
</PullQuote>
<p>
Markets responded cautiously through the morning session. The pound traded
sideways against the dollar, gilts firmed by three basis points, and the
FTSE 100 closed marginally lower as defensive sectors absorbed the
day&rsquo;s modest outflows.
</p>
<p>
In Manchester, regional officials greeted the announcement with measured
optimism. The mayor&rsquo;s office is expected to issue a formal response by
week&rsquo;s end, focusing on commitments to the trans-Pennine corridor.
</p>
</BodyText>
</Demo>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,134 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
Rule,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
import { PropsTable } from '@/components/PropsTable';
export default function RulePage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
Rule 线
</Headline>
<Subhead weight="Medium">
线 variant hairlinedoublethick
</Subhead>
<Headline weight="Low" as="h2">
API
</Headline>
<PropsTable
data={[
{
name: 'variant',
type: "'hairline' | 'double' | 'thick'",
default: "'hairline'",
description: '线型。',
},
{
name: 'orientation',
type: "'horizontal' | 'vertical'",
default: "'horizontal'",
description: '方向。',
},
{
name: 'span',
type: 'number',
description: '横向时占多少列。缺省时 1 / -1 全跨。',
},
{
name: 'className',
type: 'string',
description: '自定义 CSS 类名。',
},
{
name: 'style',
type: 'CSSProperties',
description: '自定义内联样式。',
},
]}
/>
<Headline weight="Low" as="h2">
variant
</Headline>
<Demo title="hairline / double / thick" description="水平分隔线对照。">
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<div>
<BodyText weight="Low">
<p>hairline</p>
</BodyText>
<Rule variant="hairline" />
</div>
<div>
<BodyText weight="Low">
<p>double</p>
</BodyText>
<Rule variant="double" />
</div>
<div>
<BodyText weight="Low">
<p>thick</p>
</BodyText>
<Rule variant="thick" />
</div>
</div>
</Demo>
<Headline weight="Low" as="h2">
</Headline>
<Demo
title="垂直分隔线"
description="在两栏之间放置垂直 Rule。"
code={`<div style={{ display: 'flex', gap: '1.5rem', height: '120px' }}>
<div>左栏内容</div>
<Rule variant="hairline" orientation="vertical" />
<div>右栏内容</div>
</div>`}
>
<div style={{ display: 'flex', gap: '1.5rem', height: '120px', alignItems: 'stretch' }}>
<div style={{ flex: 1 }}>
<BodyText weight="Low">
<p>
Markets responded cautiously through the morning session. The pound
traded sideways against the dollar.
</p>
</BodyText>
</div>
<Rule variant="hairline" orientation="vertical" />
<div style={{ flex: 1 }}>
<BodyText weight="Low">
<p>
Analysts at three of the City&rsquo;s largest houses framed the move as
a holding pattern.
</p>
</BodyText>
</div>
</div>
</Demo>
<Headline weight="Low" as="h2">
使
</Headline>
<BodyText weight="Low">
<p>
Rule Section <code>gridColumn: 1 / -1</code>
<code>span</code> prop Rule
flex
</p>
</BodyText>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,258 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
Rule,
} from '@newspaperui/components';
import { CodeBlock } from '@/components/CodeBlock';
export default function ResponsivePage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
NewspaperUI CSS media query + container query
JavaScript
</Subhead>
<Headline weight="Low" as="h2">
</Headline>
<BodyText>
<p>
</p>
</BodyText>
<div style={{ overflowX: 'auto', margin: '1rem 0 2rem' }}>
<table
style={{
width: '100%',
borderCollapse: 'collapse',
fontFamily: 'var(--font-family-meta)',
fontSize: '13px',
}}
>
<thead>
<tr style={{ borderBottom: '2px solid var(--nui-rule-decorative)' }}>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
</tr>
</thead>
<tbody>
<tr style={{ borderBottom: '1px solid var(--nui-rule-hairline)' }}>
<td style={{ padding: '0.5rem' }}>Mobile</td>
<td style={{ padding: '0.5rem' }}>&lt; 768px</td>
<td style={{ padding: '0.5rem' }}>1 </td>
<td style={{ padding: '0.5rem' }}>BodyText columns=1</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--nui-rule-hairline)', background: 'var(--nui-bg-surface)' }}>
<td style={{ padding: '0.5rem' }}>Tablet</td>
<td style={{ padding: '0.5rem' }}>768px - 1024px</td>
<td style={{ padding: '0.5rem' }}>12 </td>
<td style={{ padding: '0.5rem' }}>BodyText columns=2</td>
</tr>
<tr style={{ borderBottom: '1px solid var(--nui-rule-hairline)' }}>
<td style={{ padding: '0.5rem' }}>Desktop</td>
<td style={{ padding: '0.5rem' }}>&gt; 1024px</td>
<td style={{ padding: '0.5rem' }}>24 </td>
<td style={{ padding: '0.5rem' }}></td>
</tr>
</tbody>
</table>
</div>
<Headline weight="Low" as="h2">
</Headline>
<BodyText>
<p>
NewspaperUI 使 JavaScript responsive CSS
media query container query
</p>
<p>
Section <code>gridTemplateColumns</code> inline style
CSS
</p>
</BodyText>
<CodeBlock
title="自定义响应式 CSS"
language="css"
code={`/* 在你的 globals.css 或组件 CSS 中 */
@media (max-width: 768px) {
.nui-section {
grid-template-columns: 1fr !important;
}
.nui-article {
grid-column: span 1 !important;
}
}
@media (min-width: 769px) and (max-width: 1024px) {
.nui-section[data-columns="24"] {
grid-template-columns: repeat(12, 1fr) !important;
}
}`}
/>
<BodyText>
<p>
使 container query Section
</p>
</BodyText>
<CodeBlock
title="Container Query 方案"
language="css"
code={`/* 给 Section 添加 container-type */
.nui-section {
container-type: inline-size;
}
@container (max-width: 600px) {
.nui-article {
grid-column: 1 / -1 !important;
}
}`}
/>
<Rule variant="hairline" />
<Headline weight="Low" as="h2">
</Headline>
<BodyText weight="Low">
<p>
使
DevTools responsive mode
</p>
</BodyText>
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem', margin: '1.5rem 0' }}>
<div>
<div
className="nui-small-caps"
style={{
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
letterSpacing: '0.08em',
marginBottom: '0.5rem',
}}
>
Desktop (1024px+) &mdash; 24
</div>
<div
style={{
border: '1px solid var(--nui-rule-hairline)',
padding: '1rem',
background: 'var(--nui-bg-surface)',
}}
>
<Layout columns={24} padding="0" maxWidth="100%">
<Section columns={24}>
<Article span={6}>
<Headline weight="Low" as="h4">Briefing</Headline>
<BodyText weight="Low"><p>Short news items.</p></BodyText>
</Article>
<Article span={12}>
<Headline weight="Medium" as="h4">Lead Story</Headline>
<BodyText><p>Main article content with full detail.</p></BodyText>
</Article>
<Article span={6}>
<Headline weight="Low" as="h4">Markets</Headline>
<BodyText weight="Low"><p>FTSE, gilts, sterling.</p></BodyText>
</Article>
</Section>
</Layout>
</div>
</div>
<div>
<div
className="nui-small-caps"
style={{
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
letterSpacing: '0.08em',
marginBottom: '0.5rem',
}}
>
Tablet (768-1024px) &mdash; 12
</div>
<div
style={{
border: '1px solid var(--nui-rule-hairline)',
padding: '1rem',
background: 'var(--nui-bg-surface)',
maxWidth: '600px',
}}
>
<Layout columns={12} padding="0" maxWidth="100%">
<Section columns={12}>
<Article span={6}>
<Headline weight="Low" as="h4">Lead Story</Headline>
<BodyText weight="Low"><p>Main article.</p></BodyText>
</Article>
<Article span={6}>
<Headline weight="Low" as="h4">Secondary</Headline>
<BodyText weight="Low"><p>Supporting content.</p></BodyText>
</Article>
</Section>
</Layout>
</div>
</div>
<div>
<div
className="nui-small-caps"
style={{
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
letterSpacing: '0.08em',
marginBottom: '0.5rem',
}}
>
Mobile (&lt;768px) &mdash;
</div>
<div
style={{
border: '1px solid var(--nui-rule-hairline)',
padding: '1rem',
background: 'var(--nui-bg-surface)',
maxWidth: '375px',
}}
>
<Layout columns={1} padding="0" maxWidth="100%">
<Section columns={1}>
<Article span={1}>
<Headline weight="Low" as="h4">Lead Story</Headline>
<BodyText weight="Low"><p>Full-width single column.</p></BodyText>
</Article>
<Article span={1}>
<Headline weight="Low" as="h4">Secondary</Headline>
<BodyText weight="Low"><p>Stacked below.</p></BodyText>
</Article>
</Section>
</Layout>
</div>
</div>
</div>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,236 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
Rule,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
export default function SpanningPage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
24
</Subhead>
</Article>
</Section>
<Rule variant="hairline" />
<Section columns={24}>
<Article span={24}>
<Headline weight="Low" as="h2">
8 + 16
</Headline>
</Article>
</Section>
<Demo title="8 + 16" description="短讯 + 主稿。">
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={8}>
<Headline weight="Low" as="h3">
Briefing
</Headline>
<BodyText weight="Low">
<p>
Markets responded cautiously through the morning session. The pound
traded sideways against the dollar.
</p>
</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium" as="h3">
Infrastructure Review Tabled
</Headline>
<BodyText>
<p>
Whitehall officials confirmed late Tuesday that the long-anticipated
review of national infrastructure funding will be tabled before the
recess. The 248-page document recommends a recalibration of regional
priorities.
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
<Section columns={24}>
<Article span={24}>
<Headline weight="Low" as="h2">
6 + 12 + 6
</Headline>
</Article>
</Section>
<Demo title="6 + 12 + 6" description="三栏对称布局,中间主体 + 两侧辅助。">
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={6}>
<Headline weight="Low" as="h3">
Weather
</Headline>
<BodyText weight="Low">
<p>Partly cloudy, 18C. Wind NW 12 mph. Sunset 20:47.</p>
</BodyText>
</Article>
<Article span={12}>
<Headline weight="Medium" as="h3">
Chancellor Outlines Spending Envelope
</Headline>
<BodyText>
<p>
The chancellor confirmed a three-year spending envelope that prioritizes
rail electrification and regional connectivity. The announcement was
greeted with cautious optimism across the House.
</p>
</BodyText>
</Article>
<Article span={6}>
<Headline weight="Low" as="h3">
Markets
</Headline>
<BodyText weight="Low">
<p>FTSE 100: 7,842 (-0.2%). Gilts +3bp. Sterling flat.</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
<Section columns={24}>
<Article span={24}>
<Headline weight="Low" as="h2">
12 + 12
</Headline>
</Article>
</Section>
<Demo title="12 + 12" description="对称双栏,适合对比报道或并列文章。">
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={12}>
<Headline weight="Low" as="h3">
View from Westminster
</Headline>
<BodyText>
<p>
Critics inside the cabinet caution that the timing risks overshadowing
the chancellor&rsquo;s autumn statement. The opposition has called for a
full debate before any commitments are made.
</p>
</BodyText>
</Article>
<Article span={12}>
<Headline weight="Low" as="h3">
View from the Regions
</Headline>
<BodyText>
<p>
In Manchester, regional officials greeted the announcement with measured
optimism. The mayor&rsquo;s office is expected to issue a formal response
by week&rsquo;s end.
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
<Section columns={24}>
<Article span={24}>
<Headline weight="Low" as="h2">
4 + 16 + 4
</Headline>
</Article>
</Section>
<Demo title="4 + 16 + 4" description="窄侧栏 + 宽主体,适合带注释的长文。">
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={4}>
<BodyText weight="Low">
<p style={{ fontStyle: 'italic', fontSize: '12px' }}>
Editor&rsquo;s note: This report was filed before the evening session.
</p>
</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium" as="h3">
A Standing Technical Commission
</Headline>
<BodyText>
<p>
Beyond the immediate fiscal arithmetic, the review&rsquo;s most
consequential proposal may be its quietest: a standing technical
commission, modeled on Australia&rsquo;s Infrastructure Australia, to
depoliticize project sequencing.
</p>
</BodyText>
</Article>
<Article span={4}>
<BodyText weight="Low">
<p style={{ fontStyle: 'italic', fontSize: '12px' }}>
Related: &ldquo;How Australia reformed infrastructure planning&rdquo;
&mdash; p. 12
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
<Section columns={24}>
<Article span={24}>
<Headline weight="Low" as="h2">
24
</Headline>
</Article>
</Section>
<Demo title="24 全宽" description="单篇全宽文章,适合社论或特稿。">
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={24}>
<Headline weight="High" as="h3">
The Quiet Revolution in Treasury Forecasting
</Headline>
<BodyText columns={4} dropCap>
<p>
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.
</p>
<p>
Markets responded cautiously through the morning session. The pound
traded sideways against the dollar, gilts firmed by three basis points,
and the FTSE 100 closed marginally lower as defensive sectors absorbed
the day&rsquo;s modest outflows.
</p>
<p>
In Manchester, regional officials greeted the announcement with measured
optimism. The mayor&rsquo;s office is expected to issue a formal response
by week&rsquo;s end, focusing on commitments to the trans-Pennine
corridor and the long-deferred upgrade of suburban tram capacity.
</p>
<p>
Beyond the immediate fiscal arithmetic, the review&rsquo;s most
consequential proposal may be its quietest: a standing technical
commission to depoliticize project sequencing. Whether that body acquires
teeth will be determined by the legislation expected in the spring.
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
</Layout>
);
}
@@ -0,0 +1,225 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
} from '@newspaperui/components';
import { Demo } from '@/components/Demo';
import { PropsTable } from '@/components/PropsTable';
export default function GridSystemPage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
24 Layout Section grid-template-columns
Article grid-column span CSS Grid
</Subhead>
<Headline weight="Low" as="h2">
24
</Headline>
<BodyText>
<p>
24 24
2 / 3 / 4 / 6 / 8 / 12 1/32/31/43/4
</p>
</BodyText>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(24, 1fr)',
gap: '4px',
margin: '1.5rem 0 2rem',
}}
>
{Array.from({ length: 24 }, (_, i) => (
<div
key={i}
style={{
background: 'var(--nui-bg-surface)',
border: '1px solid var(--nui-rule-hairline)',
textAlign: 'center',
padding: '0.5rem 0',
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
letterSpacing: '0.04em',
}}
>
{i + 1}
</div>
))}
</div>
<Headline weight="Low" as="h2">
Layout API
</Headline>
<BodyText>
<p>
<code>Layout</code> LayoutContextSection
useLayout clamp
</p>
</BodyText>
<PropsTable
data={[
{
name: 'columns',
type: 'number',
default: '24',
description: '栅格总列数。决定子 Section 的最大 columns。',
},
{
name: 'maxWidth',
type: 'string',
default: "'1280px'",
description: '页面最大宽度。',
},
{
name: 'padding',
type: 'string',
default: "'var(--nui-space-6)'",
description: '页面内边距。',
},
{
name: 'theme',
type: "'light' | 'dark'",
description:
'强制主题;省略时跟随 documentElement.dataset.theme。',
},
{
name: 'children',
type: 'ReactNode',
required: true,
description: '子内容(通常是若干 Section)。',
},
]}
/>
<Headline weight="Low" as="h2">
Section API
</Headline>
<BodyText>
<p>
<code>Section</code>
<code>display: grid; grid-template-columns: repeat(N, 1fr)</code>
Article grid-column span
</p>
</BodyText>
<PropsTable
data={[
{
name: 'columns',
type: 'number',
required: true,
description: 'Section 内部栅格列数(≤ Layout.columns)。',
},
{
name: 'gap',
type: 'string',
default: "'var(--nui-gutter)'",
description: '栏间间距。',
},
{
name: 'breakable',
type: 'boolean',
default: 'true',
description: '允许打印时跨页断开。',
},
{
name: 'divider',
type: "'none' | 'top' | 'bottom' | 'both'",
default: "'none'",
description: '上下方向的 hairline 分隔线。',
},
]}
/>
<Headline weight="Low" as="h2">
Article API
</Headline>
<BodyText>
<p>
<code>Article</code> Section grid-column span N
span Section
</p>
</BodyText>
<PropsTable
data={[
{
name: 'span',
type: 'number',
description: '跨多少列(0 &lt; span ≤ Section.columns)。',
},
{
name: 'breakable',
type: 'boolean',
default: 'true',
description: '允许打印时跨页断开。',
},
{
name: 'children',
type: 'ReactNode',
required: true,
description: '文章内容。',
},
]}
/>
<Headline weight="Low" as="h2">
</Headline>
<Demo
title="8 + 16 跨栏"
description="左侧短讯 8 列 + 右侧主稿 16 列。"
code={`<Layout columns={24}>
<Section columns={24}>
<Article span={8}>
<Headline weight="Low">短讯</Headline>
<BodyText>...</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium">主稿</Headline>
<BodyText>...</BodyText>
</Article>
</Section>
</Layout>`}
>
<Layout columns={24} padding="0">
<Section columns={24}>
<Article span={8}>
<Headline weight="Low" as="h3">
</Headline>
<BodyText weight="Low">
<p> 8 </p>
</BodyText>
</Article>
<Article span={16}>
<Headline weight="Medium" as="h3">
稿
</Headline>
<BodyText>
<p>
16 稿
</p>
</BodyText>
</Article>
</Section>
</Layout>
</Demo>
</Article>
</Section>
</Layout>
);
}
+19
View File
@@ -0,0 +1,19 @@
import { Sidebar } from '../../components/Sidebar';
export default function DocsLayout({ children }: { children: React.ReactNode }) {
return (
<div style={{ display: 'flex', minHeight: '100vh' }}>
<Sidebar />
<main
style={{
flex: 1,
padding: '2rem 3rem',
maxWidth: 'calc(100% - 240px)',
overflow: 'auto',
}}
>
{children}
</main>
</div>
);
}
+266
View File
@@ -0,0 +1,266 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
Kicker,
BodyText,
Quote,
Byline,
Dateline,
Caption,
} from '@newspaperui/components';
import { visualWeights, resolveFontSize } from '@newspaperui/theme';
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 longText2 = `Markets responded cautiously through the morning session. The pound traded sideways against the dollar, gilts firmed by three basis points, and the FTSE 100 closed marginally lower as defensive sectors absorbed the day's modest outflows. Analysts at three of the City's largest houses framed the move as a holding pattern, awaiting further detail on the spending envelope rather than a verdict on its substance.`;
const longText3 = `In Manchester, regional officials greeted the announcement with measured optimism. "We have lobbied for this kind of clarity for the better part of a decade," said one senior figure who requested anonymity to discuss internal deliberations. The mayor's office is expected to issue a formal response by week's end, focusing on commitments to the trans-Pennine corridor and the long-deferred upgrade of suburban tram capacity.`;
const longText4 = `Beyond the immediate fiscal arithmetic, the review's most consequential proposal may be its quietest: a standing technical commission, modeled on Australia's Infrastructure Australia, to depoliticize project sequencing. Whether that body acquires teeth — or settles into the advisory torpor that has consumed earlier attempts — will be determined by the legislation expected in the spring.`;
interface WeightRow {
component: string;
weight: string;
fontFamily: string;
fontSize: string;
fontWeight: number;
lineHeight: number;
color: string;
}
function buildWeightRows(): WeightRow[] {
const rows: WeightRow[] = [];
for (const [comp, weights] of Object.entries(visualWeights)) {
for (const [w, cfg] of Object.entries(weights)) {
if (!cfg) continue;
rows.push({
component: comp,
weight: w,
fontFamily: cfg.fontFamily,
fontSize: resolveFontSize(cfg.fontSize),
fontWeight: cfg.fontWeight,
lineHeight: cfg.lineHeight,
color: cfg.color,
});
}
}
return rows;
}
export default function TextPage() {
const weightRows = buildWeightRows();
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
Headline Caption
token
</Subhead>
<Headline weight="Low" as="h2">
Headline
</Headline>
<Demo title="High / Medium / Low">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div>
<Kicker>weight=&quot;High&quot;</Kicker>
<Headline weight="High" as="h3">
A Quiet Revolution in the Treasury Forecast
</Headline>
</div>
<div>
<Kicker>weight=&quot;Medium&quot;</Kicker>
<Headline weight="Medium" as="h3">
Whitehall Confirms Infrastructure Review
</Headline>
</div>
<div>
<Kicker>weight=&quot;Low&quot;</Kicker>
<Headline weight="Low" as="h3">
Briefing: regional rail commitments
</Headline>
</div>
</div>
</Demo>
<Headline weight="Low" as="h2">
Subhead
</Headline>
<Demo title="High / Medium">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Subhead weight="High">
A measured recalibration of regional priorities is expected to define the
chancellor&rsquo;s autumn agenda.
</Subhead>
<Subhead weight="Medium">
Officials emphasized that the headline figures should be read as a planning
envelope rather than a binding allocation.
</Subhead>
</div>
</Demo>
<Headline weight="Low" as="h2">
Kicker
</Headline>
<Demo title="朱红 small-caps,挂在 Headline 上方">
<div>
<Kicker>POLITICS · WHITEHALL</Kicker>
<Headline weight="Medium" as="h3">
A Standing Technical Commission, Quietly Proposed
</Headline>
</div>
</Demo>
<Headline weight="Low" as="h2">
BodyText
</Headline>
<Demo title="单栏" description="默认形态,适合中等宽度的文章正文。">
<BodyText>
<p>{longText}</p>
<p>{longText2}</p>
</BodyText>
</Demo>
<Demo
title="三栏 + 首字下沉"
description="开启 columns={3} 与 dropCap,第一段首字母自动下沉占 2-3 行。"
code={`<BodyText columns={3} dropCap>
<p>...</p>
<p>...</p>
</BodyText>`}
>
<BodyText columns={3} dropCap>
<p>{longText}</p>
<p>{longText2}</p>
<p>{longText3}</p>
<p>{longText4}</p>
</BodyText>
</Demo>
<Demo
title="两栏(无 dropCap"
description="columns={2},栏间细线由 nui-column-rule 提供。"
>
<BodyText columns={2}>
<p>{longText}</p>
<p>{longText2}</p>
<p>{longText3}</p>
</BodyText>
</Demo>
<Headline weight="Low" as="h2">
Quote
</Headline>
<Demo title="block / inline 对照">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Quote variant="block">
We have lobbied for this kind of clarity for the better part of a decade.
</Quote>
<BodyText>
<p>
<Quote variant="inline">
</Quote>
</p>
</BodyText>
</div>
</Demo>
<Headline weight="Low" as="h2">
Byline / Dateline / Caption
</Headline>
<Demo title="元信息组件">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<div>
<Byline>BY ALICE SMITH</Byline>
<Dateline>LONDON &mdash;</Dateline>
</div>
<figure style={{ margin: 0 }}>
<div
style={{
height: '160px',
background: 'var(--nui-bg-surface)',
border: '1px solid var(--nui-rule-hairline)',
}}
/>
<Caption credit="Photograph by Jane Doe">
A view of the Treasury terrace at dusk; the new commission will report
here from May.
</Caption>
</figure>
</div>
</Demo>
<Headline weight="Low" as="h2">
</Headline>
<BodyText weight="Low">
<p>
<code>visualWeights</code>
token theme
</p>
</BodyText>
<div style={{ overflowX: 'auto', margin: '1rem 0 2rem' }}>
<table
style={{
width: '100%',
borderCollapse: 'collapse',
fontFamily: 'var(--font-family-meta)',
fontSize: '13px',
}}
>
<thead>
<tr style={{ borderBottom: '2px solid var(--nui-rule-decorative)' }}>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
<th style={{ textAlign: 'left', padding: '0.5rem' }}></th>
</tr>
</thead>
<tbody>
{weightRows.map((row, idx) => (
<tr
key={`${row.component}-${row.weight}`}
style={{
borderBottom: '1px solid var(--nui-rule-hairline)',
background:
idx % 2 === 0 ? 'transparent' : 'var(--nui-bg-surface)',
}}
>
<td style={{ padding: '0.5rem' }}>{row.component}</td>
<td style={{ padding: '0.5rem' }}>{row.weight}</td>
<td style={{ padding: '0.5rem', fontSize: '11px' }}>
<code>{row.fontFamily}</code>
</td>
<td style={{ padding: '0.5rem' }}>{row.fontSize}</td>
<td style={{ padding: '0.5rem' }}>{row.fontWeight}</td>
<td style={{ padding: '0.5rem' }}>{row.lineHeight}</td>
<td style={{ padding: '0.5rem', fontSize: '11px' }}>
<code>{row.color}</code>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Article>
</Section>
</Layout>
);
}
+216
View File
@@ -0,0 +1,216 @@
'use client';
import {
Layout,
Section,
Article,
Headline,
Subhead,
BodyText,
} from '@newspaperui/components';
import { ThemeToggle } from '@/components/ThemeToggle';
interface Swatch {
token: string;
hex: string;
label?: string;
}
const textSwatches: Swatch[] = [
{ token: '--nui-text-primary', hex: '#1A1A1A', label: '标题、主文本' },
{ token: '--nui-text-body', hex: '#22201C', label: '正文' },
{ token: '--nui-text-secondary', hex: '#4A4742', label: 'Subhead、次级' },
{ token: '--nui-text-muted', hex: '#6E6A63', label: 'Caption、注释' },
{ token: '--nui-text-quote', hex: '#2E2A24', label: 'Quote 主体' },
];
const surfaceSwatches: Swatch[] = [
{ token: '--nui-bg-page', hex: '#F7F4ED', label: 'Warm off-white 页面底' },
{ token: '--nui-bg-surface', hex: '#FBF9F4', label: '次级面板背景' },
{ token: '--nui-rule-hairline', hex: '#C9C2B2', label: '细线分隔' },
{ token: '--nui-rule-decorative', hex: '#1A1A1A', label: '强调线' },
{ token: '--nui-highlight', hex: '#F2E9C8', label: '旧报纸黄' },
];
const accentSwatches: Swatch[] = [
{ token: '--nui-accent-primary', hex: '#7A1F1F', label: 'Brick red, Kicker / Masthead 强调' },
{ token: '--nui-accent-ink-blue', hex: '#1B2A4A', label: 'The Times 蓝' },
{ token: '--nui-highlight', hex: '#F2E9C8', label: '高亮底色' },
];
interface FontFamily {
token: string;
sample: string;
description: string;
}
const families: FontFamily[] = [
{
token: '--font-family-masthead',
sample: 'The Daily Chronicle',
description: 'Cormorant Garamond — 报头',
},
{
token: '--font-family-blackletter',
sample: 'The Daily Chronicle',
description: 'UnifrakturMaguntia — Blackletter preset',
},
{
token: '--font-family-display',
sample: 'A Quiet Revolution',
description: 'Source Serif 4 — Display 大字头条',
},
{
token: '--font-family-headline',
sample: 'Whitehall confirms review',
description: 'Source Serif 4 — Headline / Subhead',
},
{
token: '--font-family-body',
sample: 'In Manchester, regional officials greeted the announcement.',
description: 'Source Serif 4 — 正文',
},
{
token: '--font-family-meta',
sample: 'BY ALICE SMITH · LONDON',
description: 'Inter — small-caps 元信息',
},
];
function SwatchGrid({ swatches }: { swatches: Swatch[] }) {
return (
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
gap: '0.75rem',
margin: '1rem 0 1.5rem',
}}
>
{swatches.map((s) => (
<div
key={s.token}
style={{ border: '1px solid var(--nui-rule-hairline)', overflow: 'hidden' }}
>
<div style={{ background: s.hex, height: '64px' }} />
<div
style={{
padding: '0.5rem 0.75rem',
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
background: 'var(--nui-bg-surface)',
}}
>
<code style={{ display: 'block', fontSize: '11px' }}>{s.token}</code>
<div
style={{
color: 'var(--nui-text-muted)',
marginTop: '0.15rem',
letterSpacing: '0.04em',
}}
>
{s.hex}
{s.label ? ` · ${s.label}` : ''}
</div>
</div>
</div>
))}
</div>
);
}
export default function ThemePage() {
return (
<Layout columns={24} maxWidth="900px" padding="2rem">
<Section columns={24}>
<Article span={24}>
<Headline weight="Medium" as="h1">
</Headline>
<Subhead weight="Medium">
+ warm off-white使#000使#FFF
</Subhead>
<Headline weight="Low" as="h2">
</Headline>
<BodyText weight="Low">
<p>
#14110D
</p>
</BodyText>
<div style={{ margin: '1rem 0 2rem' }}>
<ThemeToggle />
</div>
<Headline weight="Low" as="h2">
token
</Headline>
<SwatchGrid swatches={textSwatches} />
<Headline weight="Low" as="h2">
线 token
</Headline>
<SwatchGrid swatches={surfaceSwatches} />
<Headline weight="Low" as="h2">
token
</Headline>
<SwatchGrid swatches={accentSwatches} />
<Headline weight="Low" as="h2">
</Headline>
<BodyText weight="Low">
<p>
Cormorant Garamond Source Serif 4
穿Inter small-caps Blackletter preset
UnifrakturMaguntia
</p>
</BodyText>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '1rem',
margin: '1rem 0 2rem',
}}
>
{families.map((f) => (
<div
key={f.token}
style={{
borderTop: '1px solid var(--nui-rule-hairline)',
paddingTop: '0.75rem',
}}
>
<div
className="nui-small-caps"
style={{
fontFamily: 'var(--font-family-meta)',
fontSize: '11px',
color: 'var(--nui-text-muted)',
letterSpacing: '0.08em',
marginBottom: '0.25rem',
}}
>
{f.token} &middot; {f.description}
</div>
<div
style={{
fontFamily: `var(${f.token})`,
fontSize: '32px',
color: 'var(--nui-text-primary)',
lineHeight: 1.1,
}}
>
{f.sample}
</div>
</div>
))}
</div>
</Article>
</Section>
</Layout>
);
}
@@ -0,0 +1,87 @@
'use client';
import {
Layout, Section, Article, Masthead,
Headline, Subhead, Kicker, BodyText, Byline, Dateline,
Figure, PullQuote,
} from '@newspaperui/components';
export default function BlackletterFrontPage() {
return (
<Layout columns={24} maxWidth="1200px" padding="2rem 1.5rem">
<Masthead
variant="blackletter"
title="Die Frankfurter Zeitung"
edition="Nr. 117 · 142. Jahrgang"
date="Dienstag, 19. Mai 2026"
price="€ 3,80"
/>
<Section columns={24} divider="bottom" gap="2rem" style={{ marginTop: '2rem' }}>
<Article span={6} style={{ borderRight: '1px solid var(--nui-rule-hairline)', paddingRight: '1.25rem' }}>
<Kicker>Im Blatt</Kicker>
<Headline weight="Low" as="h3" style={{ marginTop: 0 }}>
Bundestag billigt Klimapaket nach langer Debatte
</Headline>
<BodyText weight="Low">
<p>Die einstimmige Abstimmung beendet eine umstrittene Sitzungsperiode. Politik, Seite 4.</p>
</BodyText>
<hr className="nui-rule-hairline" style={{ margin: '1rem 0' }} />
<Headline weight="Low" as="h3">Industrie sieht in Reform Chance und Risiko</Headline>
<BodyText weight="Low">
<p>Verbände begrüßen die Pläne, mahnen aber Übergangsfristen für kleinere Betriebe an.
Wirtschaft, Seite 9.</p>
</BodyText>
</Article>
<Article span={18}>
<Kicker>Politik · Eilmeldung</Kicker>
<Headline weight="High">
Historischer Pakt nach langem Verhandlungsmarathon beschlossen
</Headline>
<Subhead weight="High">
Delegierte aus dreiundzwanzig Nationen einigen sich auf einen Rahmen für Zölle, Arbeit und Emissionen
</Subhead>
<Byline>Von Eleonore Witkomm und Markus Reyes</Byline>
<Figure
src="https://images.unsplash.com/photo-1551836022-aadb801c60ae?auto=format&fit=crop&w=1200&q=80"
alt="Verhandlungsführer am Konferenztisch"
caption="Die Delegationen applaudieren nach der Verabschiedung des Schlussdokuments am Montagabend."
credit="Foto: Jane Doe / Pool"
/>
<BodyText weight="High" columns={3} dropCap style={{ marginTop: '1.5rem' }}>
<p><Dateline>Brüssel </Dateline> Nach elf aufeinanderfolgenden Verhandlungstagen, die mehrere
Teilnehmer als die anstrengendsten einer Generation bezeichneten, haben Delegierte aus
dreiundzwanzig Nationen am Montag einen umfassenden Rahmen vorgelegt, der den Handel auf dem
gesamten Kontinent neu ordnen soll.</p>
<p>Das Abkommen, das noch von den nationalen Parlamenten ratifiziert werden muss, würde
Zollordnungen harmonisieren, gemeinsame Arbeitsstandards setzen und die Unterzeichner auf einen
geteilten Emissionspfad bis 2040 verpflichten. Beamte, die in die Gespräche eingeweiht waren,
sagten, der Durchbruch sei kurz vor Mitternacht gekommen.</p>
<p>Die Märkte reagierten mit verhaltenem Optimismus. Der kontinentale Composite-Index schloss
mit einem Plus von 1,2 Prozent. Die Währung legte gegenüber dem Dollar um 0,7 Prozent zu.
Anleiherenditen, die während der Verhandlungen wegen fiskalischer Sorgen gestiegen waren, kehrten
auf das Niveau vor Beginn der Gespräche zurück.</p>
</BodyText>
<PullQuote weight="High" author="Margarethe Lindqvist, Chefverhandlerin" align="left">
Ein langer Streit, der schließlich zum Gespräch wurde.
</PullQuote>
<BodyText weight="High" columns={2}>
<p>Das Rahmenabkommen sieht eine schrittweise CO-Abgabe für Schwerindustrie ab 2028 vor.
Einnahmen sollen in einen Investitionsfonds für klimafreundliche Fertigung fließen.</p>
<p>Parlamentarische Führer in drei Hauptstädten signalisierten, dass die Ratifizierung noch vor
der Sommerpause erfolgen könnte. Zwei Regierungen kündigten an, vorher Volksabstimmungen abhalten
zu wollen.</p>
</BodyText>
</Article>
</Section>
</Layout>
);
}
+28
View File
@@ -0,0 +1,28 @@
'use client';
import { useState } from 'react';
export function ThemeToggle() {
const [dark, setDark] = useState(false);
return (
<button
onClick={() => {
const next = !dark;
setDark(next);
document.documentElement.dataset.theme = next ? 'dark' : '';
}}
style={{
fontFamily: 'var(--font-family-meta)',
fontSize: '12px',
textTransform: 'uppercase',
letterSpacing: '0.08em',
padding: '0.5rem 1rem',
background: 'transparent',
border: '1px solid var(--nui-rule-decorative)',
color: 'var(--nui-text-primary)',
cursor: 'pointer',
}}
>
{dark ? 'Light Mode' : 'Dark Mode'}
</button>
);
}
+2
View File
@@ -0,0 +1,2 @@
/* Google Fonts: Cormorant Garamond (masthead), Source Serif 4 (display/headline/body), Inter (meta), UnifrakturMaguntia (blackletter preset) */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600;8..60,700&family=Inter:wght@400;500;600;700&family=UnifrakturMaguntia&display=swap');
+81
View File
@@ -0,0 +1,81 @@
/* === Drop Cap === */
.nui-drop-cap > p:first-of-type::first-letter,
.nui-drop-cap > :first-child::first-letter {
float: left;
font-family: var(--font-family-display);
font-weight: 700;
font-size: 4.2em;
line-height: 0.82;
margin: 0.05em 0.1em 0 0;
color: var(--nui-text-primary);
}
/* === Small Caps (real OpenType, not text-transform fake) === */
.nui-small-caps {
font-variant-caps: small-caps;
font-feature-settings: "smcp" 1, "c2sc" 1;
letter-spacing: 0.06em;
font-family: var(--font-family-meta);
}
/* === Paragraph Flow: 首段不缩进,后续段落首行缩进 1em,无段间空行 === */
.nui-paragraph-flow > p {
margin: 0;
orphans: 3;
widows: 3;
hyphens: auto;
break-inside: avoid-column;
}
.nui-paragraph-flow > p + p { text-indent: 1em; }
.nui-paragraph-flow > p.nui-no-indent + p { text-indent: 0; }
/* === Old-style Figures === */
.nui-osf {
font-feature-settings: "onum" 1, "kern" 1, "liga" 1;
font-variant-numeric: oldstyle-nums proportional-nums;
}
/* === Tabular / Lining Figures (for headlines, tables) === */
.nui-tnum {
font-feature-settings: "lnum" 1, "tnum" 1;
font-variant-numeric: lining-nums tabular-nums;
}
/* === Hanging Punctuation === */
.nui-hanging-punctuation { hanging-punctuation: first allow-end last; }
/* === Column Rule (for multi-column body text) === */
.nui-column-rule {
column-rule: 1px solid var(--nui-rule-hairline);
}
/* === Avoid breaking inside (used by PullQuote, Figure, h2/h3 in column flow) === */
.nui-avoid-break {
break-inside: avoid;
page-break-inside: avoid;
}
/* === Headline span all columns (used inside multi-column flow) === */
.nui-span-all-columns { column-span: all; }
/* === Masthead double-rule === */
.nui-masthead-rule-top,
.nui-masthead-rule-bottom {
height: 8px;
background:
linear-gradient(to bottom,
var(--nui-rule-decorative) 0,
var(--nui-rule-decorative) 1px,
transparent 1px,
transparent 5px,
var(--nui-rule-decorative) 5px,
var(--nui-rule-decorative) 8px
);
}
/* === Single hairline rule === */
.nui-rule-hairline {
border: 0;
border-top: 1px solid var(--nui-rule-hairline);
margin: 0;
}
+16
View File
@@ -0,0 +1,16 @@
import { describe, it, expect } from 'vitest';
import { cx } from '../cx';
describe('cx', () => {
it('joins truthy strings', () => {
expect(cx('a', 'b', 'c')).toBe('a b c');
});
it('filters out falsy values', () => {
expect(cx('a', false, 'b', null, undefined, 'c')).toBe('a b c');
});
it('returns empty string when all falsy', () => {
expect(cx(false, null, undefined)).toBe('');
});
});
+6
View File
@@ -0,0 +1,6 @@
type ClassValue = string | false | null | undefined;
/** Tiny className joiner. Filters out falsy values. */
export function cx(...args: ClassValue[]): string {
return args.filter(Boolean).join(' ');
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB