二十四節氣時間軸(React)
以二十四節氣為主體的橫向時間軸 React 元件:依日期定位當前節氣、距下一節氣倒數、四季色帶與物候飲食短句卡;內建 21 世紀近似公式計算交節日期,並具名匯出 getSolarTerms 與 currentSolarTerm,可用曆書值覆寫。
適合茶、農產、養生、日曆型網站的內容元件。24 個節氣依序排在一條可橫向捲動的軌道上,依季節分成春綠、夏朱、秋金、冬青四段色帶;元件依 date(預設今天,掛載後才取得)定位當前節氣——該節點放大、呼吸光暈並自動捲到可視範圍。上方摘要卡顯示今日節氣、距下一節氣還有幾天,以及一句物候/飲食短句(內建 24 條繁中文案,可逐條覆寫)。交節日期用 21 世紀通用近似公式(寿星公式)計算,偶有 ±1 日誤差,可以曆書值覆寫;getSolarTerms() 與 currentSolarTerm() 也單獨匯出,方便在其他地方算節氣。
npx shadcn@latest add https://webberui.com/r/solar-terms-timeline.jsonPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<SolarTermsTimeline />
安裝
npx shadcn@latest add https://webberui.com/r/solar-terms-timeline.json或在 components.json 設定 registries 後,改用 @webberui/solar-terms-timeline 安裝。
使用
import {
SolarTermsTimeline,
getSolarTerms,
currentSolarTerm,
} from "@/components/ui/solar-terms-timeline";
// 最簡用法:不給 date 就在掛載後定位「今天」
<SolarTermsTimeline onSelect={(term) => console.log(term.name, term.date)} />
// 指定日期、橫排節氣名、以曆書值覆寫近似公式的 ±1 日誤差、覆寫短句
<SolarTermsTimeline
date="2026-02-18"
orientation="horizontal"
terms={[{ name: "雨水", date: "2026-02-18" }]}
descriptions={{ 雨水: "春茶預購開跑,濕冷天請多喝溫熱的茶。" }}
/>
// 計算函式可單獨使用
getSolarTerms(2026);
// [{ index: 0, name: "小寒", en: "Minor Cold", season: "winter", date: 2026-01-05 }, …共 24 筆]
const { current, next, daysUntilNext } = currentSolarTerm(new Date(2026, 7, 18));
// current.name === "立秋"、next.name === "處暑"、daysUntilNext === 5Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
date | Date | string | 今天(掛載後取得) | 定位用日期,字串格式 YYYY-MM-DD;未提供或解析失敗時於掛載後取「今天」,SSR 與首次渲染先顯示佔位狀態 |
year | number | date 的年份 | 時間軸顯示哪一年的 24 節氣;與 date 年份不同時不點亮任何節點 |
terms | SolarTermOverride[] | — | 以曆書值覆寫特定節氣的交節日期,修正近似公式的 ±1 日誤差 |
descriptions | Record<string, string> | 內建 24 條繁中短句 | 覆寫物候/飲食短句,鍵為節氣名;未覆寫的節氣沿用內建文案 |
orientation | "vertical" | "horizontal" | "vertical" | 節點上節氣名的排版:直排或橫排 |
showSummary | boolean | true | 是否顯示上方摘要卡(今日/選中節氣、倒數、短句、回到今日) |
locale | "zh-TW" | "en" | "zh-TW" | 內建文案語系;en 時節點主標改為英文節氣名、中文名縮小顯示 |
labels | Partial<SolarTermsTimelineLabels> | — | 逐條覆寫文案,優先於 locale 的內建值 |
value | string | null | — | 受控:目前選中的節氣名;null 代表跟隨今日節氣。不提供時為非受控模式 |
defaultValue | string | — | 非受控模式的初始選中節氣名 |
onSelect | (term: SolarTerm) => void | — | 點選節點、鍵盤移動選取或按「回到今日」時回呼,附上該節氣資料 |
className | string | — | 透傳到最外層容器 |
SolarTerm
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
index | number | — | 年內序,0 為小寒、23 為冬至 |
name | string | — | 節氣名(繁中) |
en | string | — | 英文名,如 "Start of Spring" |
season | "spring" | "summer" | "autumn" | "winter" | — | 所屬季節:立春~穀雨為春、立夏~大暑為夏、立秋~霜降為秋、立冬~大寒為冬 |
date | Date | — | 交節日期(當地時間 00:00) |
SolarTermOverride
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
name | string | — | 要覆寫的節氣名,須為二十四節氣之一 |
date | string | — | 曆書日期,格式 YYYY-MM-DD;解析失敗時忽略該筆、沿用公式值 |
SolarTermStatus
currentSolarTerm(date, terms?) 的回傳值:
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
current | SolarTerm | — | 該日所處的節氣;小寒之前(1 月初)為去年冬至 |
next | SolarTerm | — | 下一個節氣;冬至之後為明年小寒 |
daysUntilNext | number | — | 距下一節氣的天數,交節當日為 0 |
daysSinceStart | number | — | 進入目前節氣已幾天,交節當日為 0 |
SolarTermsTimelineLabels
labels 可覆寫的文案欄位;含 {name}、{days}、{n} 佔位符的欄位會在渲染時代換:
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
timeline | string | "二十四節氣時間軸" | 區塊(role="region")的無障礙名稱 |
terms | string | "二十四節氣" | 節點群組(role="radiogroup")的無障礙名稱 |
current | string | "今日節氣" | 摘要卡標題:顯示今日節氣時 |
selected | string | "節氣" | 摘要卡標題:顯示使用者點選的其他節氣時 |
nextUp | string | "下一節氣" | 倒數區標題 |
dayUnit | string | "天" | 天數單位(單數) |
daysUnit | string | "天" | 天數單位(複數) |
daysUntil | string | "距「{name}」還有 {days} 天" | 選中未來節氣時的距離文案 |
daysAgo | string | "「{name}」已過 {days} 天" | 選中過去節氣時的距離文案 |
isToday | string | "今天正是「{name}」交節之日" | 定位日期恰為交節日時的文案 |
ordinal | string | "第 {n} 節氣" | 節氣序號 |
backToToday | string | "回到今日" | 回到今日節氣的按鈕文字 |
pending | string | "正在取得今天的日期…" | 掛載前尚未取得日期時的佔位文字 |
currentMark | string | "目前節氣" | 節點 aria-label 中「目前節氣」的註記 |
spring / summer / autumn / winter | string | "春" / "夏" / "秋" / "冬" | 色帶起點與摘要卡季節籤的季節名 |
getSolarTerms(year, overrides?)
getSolarTerms(year: number, overrides?: SolarTermOverride[]): SolarTerm[]——回傳該年 24 個節氣(小寒起、冬至止),依寿星公式計算,2001–2100 年準確度最佳。傳入 overrides 可用曆書值取代特定節氣的日期。
currentSolarTerm(date, terms?)
currentSolarTerm(date: Date, terms?: SolarTerm[]): SolarTermStatus——找出某日所處節氣、下一節氣與距離天數。terms 可傳入該年(已覆寫過的)節氣清單,未傳時依公式計算;跨年的「去年冬至」與「明年小寒」一律用公式算。
另外具名匯出常數 SOLAR_TERM_NAMES(24 個節氣名,依年內順序)與 SOLAR_TERM_DESCRIPTIONS(內建 24 條短句,鍵為節氣名),以及 SolarTermSeason 型別。
細節
- 日期公式:日 = int(Y × 0.2422 + C) − L。Y 為西元年後兩位(2001–2100 → 1–100),C 為各節氣的 21 世紀常數(小寒 5.4055、大寒 20.12、立春 3.87、雨水 18.73、驚蟄 5.63、春分 20.646、清明 4.81、穀雨 20.1、立夏 5.52、小滿 21.04、芒種 5.678、夏至 21.37、小暑 7.108、大暑 22.83、立秋 7.5、處暑 23.13、白露 7.646、秋分 23.042、寒露 8.318、霜降 23.438、立冬 7.438、小雪 22.36、大雪 7.18、冬至 21.94)。L 為閏年修正:1、2 月的節氣(小寒、大寒、立春、雨水)用 int((Y − 1) / 4),其餘用 int(Y / 4)——1、2 月尚未經過當年 2/29,閏日累積要少算一年;若一律用同一個 L,每逢閏年就有一批節氣整批差一天
- ±1 日誤差:近似公式偶爾與曆書差一天(已知如 2026 雨水、2016 小暑、2021 冬至等),正式產品請用
terms以曆書值覆寫;示範與 playground 都是公式值 - 跨年:時間軸是循環的——1 月初「目前節氣」其實是去年冬至,元件仍會點亮本年的冬至節點;冬至之後的「下一節氣」是明年小寒,倒數天數會正確跨年
- 選取與摘要卡:未選取時摘要卡顯示今日節氣與「距下一節氣 N 天」;點選其他節點後改顯示該節氣、距今幾天前/後與「回到今日」按鈕。
value為null代表跟隨今日節氣,受控時請在onSelect中同步 - 自動捲動:只捲動時間軸自己的水平捲軸(計算
offsetLeft後對軌道scrollTo),不用scrollIntoView——後者會連整頁一起捲,元件在視窗外時整頁會跳 - SSR:未提供
date時,伺服器端與首次渲染只列 24 個節氣名、不算日期、摘要卡顯示佔位文字;掛載後才取今天並填入日期,節點高度預留、不跳版
可及性
- 最外層為
role="region",以視覺隱藏的標題(labels.timeline)命名;軌道為role="radiogroup",24 個節點是type="button"、role="radio"的原生按鈕,aria-checked標示使用者的選取、aria-current="date"標示今日節氣,aria-label含節氣名、日期與「目前節氣」註記 - 鍵盤:roving tabindex——只有目前節點可 Tab 進入;方向鍵(左右/上下)在 24 個節點間循環移動並即時選取,
Home/End跳到小寒/冬至,焦點移動時軌道跟著捲動 - 摘要卡的短句置於
aria-live="polite"區域,切換節氣時會被螢幕閱讀器朗讀;軌道以aria-describedby指向摘要卡 - 季節色帶與光暈皆為
aria-hidden裝飾;季節不只靠顏色區分——每段色帶起點標有季節名,摘要卡的季節籤也帶文字與圖示 - 使用者系統開啟「減少動態效果」時,停用當前節點的呼吸光暈(改為靜態淡色光圈)、節點縮放與短句淡入改為即時切換,自動捲動也改用
behavior: "auto"直接跳到位置