-
@@ -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 framework’s 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 levy’s 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 accord’s 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 accord’s 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 bloc’s 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 framework’s 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 levy’s 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 accord’s 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 accord’s 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 bloc’s 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 chancellor’s 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
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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’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’s office is expected to issue a
|
||||||
|
formal response by week’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)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
“We have lobbied for this kind of clarity for the better part of
|
||||||
|
a decade.”
|
||||||
|
</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">
|
||||||
|
Image、Figure、Video、PullQuote — 报纸版面中的视觉元素。
|
||||||
|
</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><video></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’s modest outflows.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In Manchester, regional officials greeted the announcement with measured
|
||||||
|
optimism. The mayor’s office is expected to issue a formal response by
|
||||||
|
week’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 覆盖 hairline、double、thick,支持水平和垂直方向。
|
||||||
|
</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’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' }}>< 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' }}>> 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+) — 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) — 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 (<768px) — 单栏堆叠
|
||||||
|
</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’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’s office is expected to issue a formal response
|
||||||
|
by week’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’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’s most
|
||||||
|
consequential proposal may be its quietest: a standing technical
|
||||||
|
commission, modeled on Australia’s Infrastructure Australia, to
|
||||||
|
depoliticize project sequencing.
|
||||||
|
</p>
|
||||||
|
</BodyText>
|
||||||
|
</Article>
|
||||||
|
<Article span={4}>
|
||||||
|
<BodyText weight="Low">
|
||||||
|
<p style={{ fontStyle: 'italic', fontSize: '12px' }}>
|
||||||
|
Related: “How Australia reformed infrastructure planning”
|
||||||
|
— 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’s modest outflows.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In Manchester, regional officials greeted the announcement with measured
|
||||||
|
optimism. 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.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Beyond the immediate fiscal arithmetic, the review’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/3、2/3、1/4、3/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> 是顶层容器,提供 LayoutContext。Section 通过
|
||||||
|
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 < 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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="High"</Kicker>
|
||||||
|
<Headline weight="High" as="h3">
|
||||||
|
A Quiet Revolution in the Treasury Forecast
|
||||||
|
</Headline>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Kicker>weight="Medium"</Kicker>
|
||||||
|
<Headline weight="Medium" as="h3">
|
||||||
|
Whitehall Confirms Infrastructure Review
|
||||||
|
</Headline>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Kicker>weight="Low"</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’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 —</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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} · {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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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('');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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(' ');
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 831 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 778 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 757 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 204 KiB |