Initial commit

This commit is contained in:
2026-04-21 13:49:12 +08:00
commit 272d0973bd
6 changed files with 1495 additions and 0 deletions

44
CLAUDE.md Normal file
View File

@@ -0,0 +1,44 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 專案性質
單檔靜態 HTML landing page 示範專案(虛構產品 `dotlog`),用於演繹 `DESIGN.md` 的設計規範。**無 build 系統、無 npm 依賴、無測試框架、非 git 倉庫**。文案、統計數字、GitHub 連結皆為假資料,不要當成真實產品資訊處理。
## 常用指令
```bash
# 本地預覽(推薦,避開 file:// 的字體 CORS
python3 -m http.server 8000
# 或直接開檔
open index.html
```
重新產生 `screenshot-light.png` / `screenshot-dark.png`README 有完整 Playwright (Python) 腳本。**關鍵陷阱**:全頁截圖前必須手動把所有 `.fade-up` 元素加上 `visible` class否則下半部區塊會因 IntersectionObserver 尚未觸發而呈空白。
## 架構
只有三個檔案要理解:
- **`DESIGN.md`** — 設計權威來源。色彩 token、字體、間距、圓角、各區塊版面、組件樣式Button/Card/Badge/Code、動態規範、可及性要求都在這裡。**改 `index.html` 樣式前先查這份**;設計決策衝突時以 `DESIGN.md` 為準,除非使用者明確要求偏離。
- **`index.html`** — 全部 HTML + CSS + JS 都在單檔內,無外部 `.css` / `.js`。內部結構固定順序:
1. `<style>`Tokens (`:root``html.dark`) → Reset → 各區塊樣式(依 Header → Hero → Features → Steps → Apps → Pipeline → FAQ → Footer 順序排列,與 `<body>` 內的 `<section>` 順序一致)。
2. `<body>` 內每個 `<section class="section">``<!-- ============ 區塊名 ============ -->` 註解分隔,可獨立增刪不影響其他區塊。
3. 檔末兩個 IIFE主題切換`html.dark` class + `localStorage('dotlog-theme')` + `prefers-color-scheme` fallback`.fade-up` 的 IntersectionObserver。
- **`README.md`** — 使用者文件,列出區塊清單、響應式斷點、可及性重點、客製化步驟。
### 主題切換
深色主題靠 `html.dark` class 驅動,所有 CSS variable 在 `html.dark { ... }` 區塊重新定義。加新樣式時若用到顏色,**一律透過 CSS variable**`var(--foreground)` 等),不要寫死 hex否則深色模式會壞。
### 字體
`<head>` 只載入 Space Grotesk + JetBrains MonoGoogle Fonts CDN`DESIGN.md` 第 3 節列的 Merriweather 在本實作中**刻意省略**,不要自行補回。
## 編輯守則
- 改品牌色:同時更新 `:root``html.dark``--primary`(兩處),`DESIGN.md` 第 2 節也要同步。
- 主色 `#00bb7f` 只用於 CTA、logo、關鍵圖示、hover — 不做大面積填充(`DESIGN.md` §2.4)。
- 卡片靠 `1px solid var(--border)` 劃界無預設陰影hover 才浮起(`DESIGN.md` §6.2)。
- 響應式斷點對照 README「響應式斷點」表與 `DESIGN.md` §9動 grid 欄數前先確認。

336
DESIGN.md Normal file
View File

@@ -0,0 +1,336 @@
# DESIGN.md — CLI Anything 網站設計規格
> 本文件根據 https://clianything.org/zh 實機抓取的 CSS token 與 DOM 結構彙整而成,可直接作為複刻、二次開發或延伸設計的依據。
---
## 1. 設計定位Design Principle
| 項目 | 說明 |
|------|------|
| 產品類型 | 開發者工具 / 開源 Claude Code 插件 |
| 目標受眾 | AI 代理開發者、工具鏈工程師、關注自動化的工程文化社群 |
| 設計關鍵字 | **極簡、克制、技術感、綠色科技、文件驅動、清透** |
| 視覺基調 | **亮面現代極簡 + 終端機感的等寬字節奏**。不走炫技路線,以排版、留白、節制色彩呈現工程可信度 |
| 品牌人格 | 技術專業、務實、自信強調「100% 測試通過」「2k+ Stars」但語氣對開發者友善 |
---
## 2. 色彩系統Color Tokens
採用 Tailwind CSS v4 的 CSS Variables 架構,支援 Light / Dark 雙主題。**預設為淺色主題**。
### 2.1 Light Theme
| Token | HEX | 用途 |
|-------|-----|------|
| `--background` | `#fcfcfc` | 全站底色(近白但非純白,降低刺眼感) |
| `--foreground` | `#171717` | 主要文字 |
| `--card` | `#fcfcfc` | 卡片底(與背景同色,靠 border 區隔) |
| `--primary` | `#00bb7f` | **品牌主色**(翡翠綠 / emerald用於 CTA、強調、圖示 |
| `--secondary` | `#fdfdfd` | 次級區塊底 |
| `--muted` | `#ededed` | 柔和背景(如 code inline、tag |
| `--muted-foreground` | `#202020` | 柔和文字 |
| `--accent` | `#ededed` | 互動 hover 背景 |
| `--border` | `#dfdfdf` | 分隔線、卡片描邊 |
| `--ring` | `#72e3ad` | focus ring主色淡化版 |
### 2.2 Dark Theme
| Token | HEX |
|-------|-----|
| `--background` | `#121212` |
| `--foreground` | `#e2e8f0` |
| `--card` | `#171717` |
| `--primary` | `#007956`(深一階的翡翠綠) |
| `--secondary` | `#242424` |
| `--muted` | `#1f1f1f` |
| `--muted-foreground` | `#a2a2a2` |
| `--accent` | `#313131` |
| `--border` | `#292929` |
| `--ring` | `#4ade80` |
### 2.3 功能色(狀態/語意)
沿用 Tailwind 官方色票:
- 成功:`green-500 #00c758` / `green-600 #00a544`
- 警告:`amber-400 #fcbb00` / `yellow-400 #fac800`
- 危險:`red-500 #fb2c36`
- 紫(徽章):`purple-400 #c07eff`
- 中性:`zinc-500 #71717b` / `neutral-500 #737373`
### 2.4 配色原則
1. **主色只做一件事**`#00bb7f` 僅用於 CTA、LOGO、關鍵圖示、連結 hover、進度點不做大面積填充。
2. **卡片與底色同色**`card``background` 都是 `#fcfcfc`,利用 `border: 1px solid #dfdfdf` 劃分層級,形成「平面分區」質感。
3. **文字以中性灰階為主**`#171717` 主體、`#525252`neutral-600次級說明、`#a1a1a1`neutral-400輔助。
---
## 3. 字體系統Typography
### 3.1 字體家族
```css
--font-sans: "Space Grotesk", system-ui, sans-serif; /* 標題與 UI */
--font-serif: "Merriweather", Georgia, serif; /* 少量引用、強調段落 */
--font-mono: "JetBrains Mono", "Fira Code", monospace; /* 程式碼、指令、技術詞 */
```
選用邏輯:
- **Space Grotesk**:幾何人文無襯線,字腔開闊、帶一點工程感,適合技術品牌標題。
- **Merriweather**:作為對比的 serif偶爾用於強調「宣言式」文案。
- **JetBrains Mono**:開發者社群熟悉的等寬字,強化「命令列」品牌語言。
### 3.2 字級階層
| Token | Size / Line-height | 使用場景 |
|-------|--------------------|----------|
| `text-7xl` | 72 / 1.0 | Hero 主標題(桌機) |
| `text-5xl` | 48 / 1.0 | Section 大標 |
| `text-4xl` | 36 / 1.11 | Section 次級標 |
| `text-3xl` | 30 / 1.2 | 卡片內標題 |
| `text-xl` | 20 / 1.4 | Hero 副標、引言 |
| `text-base`| 16 / 1.5 | 正文 |
| `text-sm` | 14 / 1.43 | 註解、metadata |
| `text-xs` | 12 / 1.33 | 徽章、footer |
字重:`400 / 500 / 600 / 700`Hero 標題使用 `700`,一般段落 `400`,小標 `600`
---
## 4. 間距、圓角、陰影
| Token | 值 | 用途 |
|-------|----|------|
| `--spacing` | `0.25rem (4px)` | 基礎單位(全站 4 的倍數) |
| `--radius-xs` | `2px` | inline code、小標籤 |
| `--radius-2xl` | `16px` | **按鈕、卡片預設** |
| `--radius-3xl` | `24px` | Hero 容器、大型區塊 |
| `--radius-4xl` | `32px` | 特殊浮動區塊 |
| `--shadow-sm` | 輕陰影 | 卡片靜態 |
| `--shadow-lg` | 中陰影 | hover 懸浮 |
| 過渡 | `cubic-bezier(.4, 0, .2, 1) 150ms` | 全站統一 ease-in-out |
**容器寬度**:最大 `--container-6xl: 72rem (1152px)` 為內容主幅寬。
---
## 5. 頁面結構與區塊Layout
```
┌─────────────────────────────────────────────┐
│ Header (sticky, 64px) │
├─────────────────────────────────────────────┤
│ │
│ Hero — 大標 + 副標 + 雙 CTA + 徽章列 │
│ │
├─────────────────────────────────────────────┤
│ 為什麼選擇 CLI — 4 欄優勢卡片 │
├─────────────────────────────────────────────┤
│ 5 分鐘上手 — 4 步驟編號卡 │
├─────────────────────────────────────────────┤
│ 應用覆蓋 — 6 類別卡片網格 │
├─────────────────────────────────────────────┤
│ 核心特性 — 7 階段流水線(水平時間軸) │
├─────────────────────────────────────────────┤
│ FAQ — Accordion 折疊問答 │
├─────────────────────────────────────────────┤
│ Footer — 4 欄連結 + 版權 │
└─────────────────────────────────────────────┘
```
### 5.1 Header
- 高度 `64px``position: sticky; top: 0;`、背景半透明 + `backdrop-filter: blur(8px)`
-LogoPNG約 32px 高) + 品牌字「CLI Anything」。
- 右:`功能特性``在 GitHub 查看`(後者為 `primary` 填色按鈕)。
- 底部 `border-bottom: 1px solid #dfdfdf`
### 5.2 Hero
- 垂直置中、上下 padding ≈ `96px`
- 主標題:`text-7xl / 700 / tracking-tight`,行距 1.0,最多兩行。
- 副標題:`text-xl / 400 / neutral-600`,最大寬度 `42rem` 置中。
- 徽章列:`⭐ GitHub 2k+ Stars · 1,436 個測試全部通過 · 開源免費` — 使用 `text-sm`、灰階、中點分隔。
- 雙 CTA
- 主:`立即開始``bg-primary #00bb7f`、白字、`rounded-2xl``px-6 py-3`)。
- 次:`在 GitHub 查看``outline` 風、透明底、border-neutral
### 5.3 Feature Grid為什麼選擇 CLI
- 4 欄(桌機)/ 2 欄(平板)/ 1 欄(手機)。
- 卡片:`bg-card``border``rounded-2xl`、內距 `24px`
- 結構:頂部小圖示(`primary`24px+ 標題 + 描述。
### 5.4 快速上手步驟
- 水平排列的編號卡1→2→3→4每張卡含
- 大型編號(`text-5xl` + `primary` 色 + `font-mono`)。
- 步驟標題。
- 終端指令(背景 `#ededed``JetBrains Mono``rounded-xs`、內距 `8px 12px`)。
### 5.5 應用覆蓋Apps Grid
- 6 格卡片排版,每格一大類,內含 chip tag 列示支援的軟體名稱。
- Tag 樣式:`bg-muted``text-xs``rounded-xs``px-2 py-0.5`
### 5.6 7 階段流水線
- 水平時間軸7 個節點以 `primary` 圓點 + 連線呈現。
- 桌機水平、手機垂直折疊。
### 5.7 FAQ
- Accordion 組件,`border-bottom` 逐列劃分點擊展開平滑過渡150ms
- 問題使用 `font-semibold`,展開內容使用 `muted-foreground`
### 5.8 Footer
- 4 欄:品牌敘述 / 導航 / 資源 / 法律。
- 背景同 `background`,頂部 `border-top`
- 版權置中,`text-sm / neutral-500`
---
## 6. 組件樣式規範Components
### 6.1 Button
```
/* Primary */
background: #00bb7f;
color: #fff;
padding: 12px 24px;
border-radius: 16px;
font-weight: 600;
transition: transform 150ms, box-shadow 150ms;
hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
/* Secondary */
background: transparent;
color: #171717;
border: 1px solid #dfdfdf;
hover { background: #ededed; }
/* Ghost (Header link) */
padding: 8px 12px;
hover { color: #00bb7f; }
```
### 6.2 Card
```
background: #fcfcfc;
border: 1px solid #dfdfdf;
border-radius: 16px;
padding: 24px;
/* 無預設陰影hover 才浮起 */
hover { box-shadow: var(--shadow-lg); border-color: #00bb7f33; }
```
### 6.3 Badge / Tag
```
background: #ededed;
color: #202020;
font-size: 12px;
padding: 2px 8px;
border-radius: 2px;
font-family: var(--font-mono);
```
### 6.4 Inline Code / Command
```
font-family: "JetBrains Mono";
background: #ededed;
color: #171717;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.875em;
```
---
## 7. 互動與動態Motion
| 項目 | 規格 |
|------|------|
| 預設過渡 | `150ms cubic-bezier(.4, 0, .2, 1)` |
| 按鈕 hover | `translateY(-1px)` + shadow-lg |
| 卡片 hover | border-color 轉成 primary帶透明度+ shadow-lg |
| Accordion | `max-height` / `opacity` 同時過渡 200ms |
| Scroll 進入 | 建議使用 `fade-up`opacity 0→1, translateY 8px→0, 400ms |
| 載入動畫 | `--animate-spin: spin 1s linear infinite` 供載入指示器使用 |
**原則**不使用視差、粒子、3D 等重特效。動態只為回饋與引導視線,不喧賓奪主。
---
## 8. 圖示與圖片
- **Logo**PNG 去背單色可轉白Footer或原色Header
- **圖示**:線性風格,`stroke-width: 1.5`,顏色 `#00bb7f``#171717`,尺寸 `24px`
- **Hero 背景**:可選擇純色(預設)或加入極淡的 grid pattern / noise textureopacity ≤ 5%)增加質感,避免放大型照片打破極簡感。
---
## 9. 響應式斷點Breakpoints
沿用 Tailwind 預設:
| 斷點 | 寬度 | 典型佈局變化 |
|------|------|---------------|
| `sm` | 640px | 單欄→雙欄起點 |
| `md` | 768px | 2 欄網格 |
| `lg` | 1024px | 4 欄 feature 網格 |
| `xl` | 1280px | 完整桌機版7xl 大標) |
| `2xl`| 1536px | 容器置中、不再放大 |
手機版 Hero 主標降為 `text-4xl`padding 壓縮至 `48px`
---
## 10. 語氣與文案風格Voice & Tone
- **務實、技術、自信**以數字佐證主張1,436、2k+、100%)。
- **動詞開場**:「讓」、「告別」、「轉化」、「發現」。
- **開發者密語**:允許出現 `--json``--help``/plugin install` 等技術詞彙,不做科普式解釋,相信讀者能看懂。
- **避免行銷套話**:不使用「革命性」、「顛覆」、「一站式」等空泛形容詞。
---
## 11. 可及性Accessibility
- 正文對比率 `#171717` on `#fcfcfc` ≈ 16:1超過 WCAG AAA。
- CTA `#fff` on `#00bb7f` 對比 ≈ 3.1:1符合 AA Large。建議小字按鈕可加粗字重或增加 border。
- 所有互動元素必須具備 `focus-visible` ring使用 `--ring: #72e3ad``outline: 2px solid``outline-offset: 2px`)。
- Accordion 使用語意化 `<details>` 或具 `aria-expanded` 的按鈕。
- 所有 icon-only button 附 `aria-label`
---
## 12. 技術棧(參考)
| 層 | 技術 |
|----|------|
| 框架 | Next.jsApp Router`/_next/static/chunks` 推斷) |
| 樣式 | Tailwind CSS v4CSS Variables + `@layer theme` |
| 字體載入 | `next/font` 本地自管Space Grotesk / Merriweather / JetBrains Mono |
| 主題 | CSS 變數切換Light / Dark可能由 `class="dark"` 控制) |
| 部署 | 靜態化 Next.js |
---
## 13. 複刻檢核表Checklist
撰寫實作時可逐項對照:
- [ ] 背景色用 `#fcfcfc` 而非 `#ffffff`
- [ ] 主色只用於 CTA / Logo / 強調點,不大面積填充
- [ ] 卡片靠 `1px border` 劃界,而非陰影
- [ ] 標題使用 Space Grotesk、指令一律 JetBrains Mono
- [ ] 按鈕圓角 `16px (rounded-2xl)`
- [ ] Hover 有輕微上浮 + 陰影
- [ ] 所有過渡 150ms ease-in-out
- [ ] 容器最寬 `1152px` 置中
- [ ] 數字指標以 mono + 中點分隔列於 Hero 下方
- [ ] Footer 含 4 欄、版權置中
- [ ] 支援 Light / Dark 主題切換
- [ ] 所有互動元素可見 focus ring
---
_以上規格為觀察 clianything.org/zh 正式站點之設計萃取,可作為同風格專案的設計基礎;實際實作時若取得原始設計稿,以設計稿為準。_

180
README.md Normal file
View File

@@ -0,0 +1,180 @@
# dotlog — Landing Page Demo
一個單檔 HTML 的產品 landing page 範例,遵循 [`DESIGN.md`](./DESIGN.md) 中萃取自 [clianything.org/zh](https://clianything.org/zh) 的設計規範(極簡、綠色科技感、開發者導向)。
> 這是一個**示範用的假想產品**`dotlog` — 一個「一行指令把所有日誌管線串起來」的 CLI 工具。所有文案、統計數字、GitHub 連結皆為虛構,僅作為設計實作的展示。
---
## 預覽
| 淺色主題 | 深色主題 |
|---------|---------|
| ![Light mode](./screenshot-light.png) | ![Dark mode](./screenshot-dark.png) |
---
## 快速開始
不需要任何 build 工具或 npm 依賴,直接用瀏覽器打開即可:
```bash
open index.html
```
若字體載入遇到 CORS 問題(某些瀏覽器對 `file://` 較嚴格),起一個本地靜態伺服器:
```bash
python3 -m http.server 8000
# 打開 http://localhost:8000
```
---
## 檔案結構
```
.
├── DESIGN.md # 設計規格(色彩 / 字體 / 版面 / 組件 tokens
├── index.html # 單檔實作HTML + CSS + 最少量 JS
├── screenshot-light.png # 淺色主題預覽
├── screenshot-dark.png # 深色主題預覽
└── README.md # 你正在看的這個檔案
```
---
## 頁面區塊
`DESIGN.md` 第 5 節,共 8 個區塊:
1. **Header** — Sticky、半透明 + `backdrop-filter`、含深色模式 toggle
2. **Hero** — 大標 + 副標 + 徽章列 + 雙 CTA含淡綠 radial gradient 背景
3. **為什麼選擇 dotlog** — 4 欄特色卡(零配置 / 全格式 / 低資源 / 原生整合)
4. **5 分鐘上手** — 4 步驟編號卡,含終端指令 code block
5. **應用覆蓋** — 6 類別卡片網格Web / 容器 / DB / 雲端 / MQ / 監控)
6. **7 階段流水線** — 水平時間軸(手機自動翻轉為垂直)
7. **FAQ** — 原生 `<details>` accordion`+/` 圖示切換
8. **Footer** — 4 欄連結 + MIT 版權
---
## 技術重點
### 色彩系統
使用 CSS Variables對應 `DESIGN.md` 第 2 節:
```css
:root {
--background: #fcfcfc;
--foreground: #171717;
--primary: #00bb7f; /* 品牌綠,僅用於 CTA、重點、hover */
--border: #dfdfdf;
/* ... */
}
html.dark {
--background: #121212;
--foreground: #e2e8f0;
/* ... */
}
```
### 字體
Google Fonts CDN 載入:
- **Space Grotesk**400/500/600/700— 標題與 UI
- **JetBrains Mono**400/500/600— 指令、徽章、標籤
`DESIGN.md` 原規格含 Merriweather本實作為保持簡單已省略。
### 互動
| 行為 | 實作 |
|------|------|
| 深色模式 | toggle 按鈕 + `localStorage` 記憶 + 預設跟隨 `prefers-color-scheme` |
| Scroll fade-up | `IntersectionObserver`threshold 0.08`rootMargin: -40px bottom` |
| FAQ 展開 | 原生 `<details>`,無需額外 JS |
| Header 模糊 | `backdrop-filter: blur(10px)`(含 `-webkit-` 前綴) |
| 卡片 hover | `translateY(-2px)` + `shadow-lg` + border 轉綠 |
### 響應式斷點
| 寬度 | 變化 |
|------|------|
| `≥ 1024px` | 4 欄 feature grid、水平 7 階段流水線 |
| `7681023px` | 2 欄 grid、水平流水線仍保留 |
| `560767px` | 2 欄 app grid、流水線垂直時間軸 |
| `< 560px` | 單欄、Hero padding 壓縮、Header 僅保留主 CTA |
### 可及性
- 正文對比率 `#171717 on #fcfcfc` ≈ 16:1WCAG AAA
- 所有互動元素具備 `focus-visible` ring`outline: 2px solid var(--ring)`
- Icon-only 按鈕(主題 toggle`aria-label`
- Accordion 使用語意化 `<details>` / `<summary>`
- `prefers-reduced-motion` 下停用 fade-up 動畫與平滑捲動
---
## 客製化
### 改品牌色
打開 `index.html`,找 `:root { --primary: #00bb7f }`,換成你要的色碼即可。深色主題同時修改 `html.dark { --primary: ... }`
### 改文案
所有文字寫在 HTML 中(非 JS直接用編輯器搜尋替換。區塊之間以 HTML 註解 `<!-- ============ Hero ============ -->` 分隔,方便定位。
### 加 / 刪區塊
每個 `<section class="section">` 是一個獨立區塊,直接複製或刪除整個 `<section>` 節點即可,不會影響其他區塊。
### 換字體
修改 `<head>` 內的 Google Fonts `<link>``--font-sans` / `--font-mono` 變數。
---
## 重新產生截圖
本倉庫內的截圖由 PlaywrightPython自動產生
```bash
# 需先安裝 playwright
pip install playwright
playwright install chromium
# 截圖腳本(可存為 screenshot.py
python3 -c "
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
ctx = await browser.new_context(viewport={'width': 1280, 'height': 900}, device_scale_factor=2)
page = await ctx.new_page()
await page.goto('file://$(pwd)/index.html', wait_until='networkidle')
await page.evaluate(\"document.querySelectorAll('.fade-up').forEach(e => e.classList.add('visible'))\")
await page.wait_for_timeout(400)
await page.screenshot(path='screenshot-light.png', full_page=True)
await page.evaluate(\"document.documentElement.classList.add('dark')\")
await page.wait_for_timeout(300)
await page.screenshot(path='screenshot-dark.png', full_page=True)
await browser.close()
asyncio.run(main())
"
```
> ⚠️ 截圖時需手動將 `.fade-up` 元素加上 `visible` class因為 `IntersectionObserver` 只在元素進入視窗時觸發,全頁截圖會從頁頂往下卷,下半部元素在截圖瞬間尚未「進入視窗」,導致底部區塊空白。
---
## 授權
本示範專案採 MIT 授權自由使用DESIGN.md 的靈感來源為 [clianything.org](https://clianything.org) 公開網站的觀察萃取,非官方產出。

935
index.html Normal file
View File

@@ -0,0 +1,935 @@
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>dotlog — 把所有日誌管線串成一行</title>
<meta name="description" content="dotlog 是為工程師而生的 CLI 工具:一條指令完成擷取、過濾、聚合、輸出。" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<style>
/* ===================== Tokens ===================== */
:root {
--background: #fcfcfc;
--foreground: #171717;
--card: #fcfcfc;
--primary: #00bb7f;
--primary-soft: rgba(0, 187, 127, 0.25);
--primary-bg: rgba(0, 187, 127, 0.10);
--secondary: #fdfdfd;
--muted: #ededed;
--muted-foreground: #525252;
--neutral-400: #a1a1a1;
--accent: #ededed;
--border: #dfdfdf;
--ring: #72e3ad;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
--shadow-lg: 0 12px 32px -14px rgba(0, 0, 0, 0.18);
--radius-xs: 4px;
--radius-md: 10px;
--radius-2xl: 16px;
--radius-3xl: 24px;
--font-sans: "Space Grotesk", system-ui, -apple-system, "Noto Sans TC", sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
--transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--container: 1152px;
}
html.dark {
--background: #121212;
--foreground: #e2e8f0;
--card: #171717;
--primary: #00bb7f;
--primary-soft: rgba(74, 222, 128, 0.30);
--primary-bg: rgba(0, 187, 127, 0.14);
--secondary: #242424;
--muted: #1f1f1f;
--muted-foreground: #a2a2a2;
--accent: #313131;
--border: #292929;
--ring: #4ade80;
--shadow-lg: 0 12px 32px -14px rgba(0, 0, 0, 0.6);
}
/* ===================== Reset ===================== */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--font-sans);
background: var(--background);
color: var(--foreground);
font-size: 16px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
transition: background var(--transition), color var(--transition);
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; padding: 0; }
code { font-family: var(--font-mono); background: var(--muted); padding: 1px 6px; border-radius: var(--radius-xs); font-size: 0.875em; }
.container {
max-width: var(--container);
margin: 0 auto;
padding: 0 24px;
}
/* ===================== Header ===================== */
.site-header {
position: sticky;
top: 0;
z-index: 50;
height: 64px;
display: flex;
align-items: center;
background: color-mix(in srgb, var(--background) 78%, transparent);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
}
.site-header .container {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-weight: 700;
font-size: 18px;
letter-spacing: -0.01em;
}
.brand-dot {
width: 10px; height: 10px;
border-radius: 50%;
background: var(--primary);
box-shadow: 0 0 10px var(--primary);
}
.nav {
display: flex;
align-items: center;
gap: 6px;
}
.nav > a {
padding: 8px 12px;
font-size: 14px;
color: var(--muted-foreground);
border-radius: 8px;
transition: color var(--transition);
}
.nav > a:hover { color: var(--primary); }
.theme-toggle {
width: 36px; height: 36px;
border-radius: var(--radius-md);
display: grid;
place-items: center;
border: 1px solid var(--border);
transition: background var(--transition), border-color var(--transition);
margin: 0 4px;
}
.theme-toggle:hover { background: var(--accent); border-color: var(--primary-soft); }
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle .sun { display: none; }
html.dark .theme-toggle .sun { display: block; }
html.dark .theme-toggle .moon { display: none; }
.nav-github {
padding: 8px 14px !important;
font-size: 13.5px !important;
border-radius: 12px !important;
}
@media (max-width: 639px) {
.nav > a:not(.nav-github) { display: none; }
}
/* ===================== Buttons ===================== */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
border-radius: var(--radius-2xl);
font-weight: 600;
font-size: 15px;
transition: transform var(--transition), box-shadow var(--transition), background var(--transition), border-color var(--transition), color var(--transition);
border: 1px solid transparent;
cursor: pointer;
white-space: nowrap;
}
.btn-primary {
background: var(--primary);
color: #fff;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-lg);
}
.btn-outline {
background: transparent;
color: var(--foreground);
border-color: var(--border);
}
.btn-outline:hover {
background: var(--accent);
border-color: var(--primary);
}
/* ===================== Hero ===================== */
.hero {
padding: 96px 0 72px;
text-align: center;
position: relative;
overflow: hidden;
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 50% 0%, var(--primary-bg), transparent 70%);
pointer-events: none;
}
.hero > * { position: relative; }
.hero h1 {
font-size: clamp(36px, 7.2vw, 72px);
line-height: 1.02;
letter-spacing: -0.03em;
font-weight: 700;
margin: 0 0 24px;
}
.hero h1 .accent { color: var(--primary); }
.hero .subtitle {
font-size: clamp(16px, 1.5vw, 20px);
line-height: 1.55;
color: var(--muted-foreground);
max-width: 42rem;
margin: 0 auto 28px;
}
.hero-badges {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 12px 14px;
margin-bottom: 40px;
font-size: 13px;
color: var(--muted-foreground);
font-family: var(--font-mono);
}
.hero-badges .sep { color: var(--neutral-400); }
.hero-ctas {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
/* ===================== Section base ===================== */
.section {
padding: 80px 0;
border-top: 1px solid var(--border);
}
.section-head {
text-align: center;
margin-bottom: 56px;
}
.section-kicker {
font-family: var(--font-mono);
font-size: 12.5px;
color: var(--primary);
letter-spacing: 0.12em;
text-transform: uppercase;
margin-bottom: 14px;
font-weight: 500;
}
.section h2 {
font-size: clamp(30px, 4.2vw, 48px);
line-height: 1.1;
font-weight: 700;
margin: 0 0 14px;
letter-spacing: -0.02em;
}
.section-sub {
color: var(--muted-foreground);
font-size: 16.5px;
max-width: 40rem;
margin: 0 auto;
line-height: 1.6;
}
/* ===================== Grids ===================== */
.grid-4 {
display: grid;
gap: 20px;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1023px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 559px) { .grid-4 { grid-template-columns: 1fr; } }
.grid-steps {
display: grid;
gap: 20px;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1023px) { .grid-steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 559px) { .grid-steps { grid-template-columns: 1fr; } }
.grid-apps {
display: grid;
gap: 20px;
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 767px) { .grid-apps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 479px) { .grid-apps { grid-template-columns: 1fr; } }
/* ===================== Card ===================== */
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-2xl);
padding: 24px;
transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.card:hover {
border-color: var(--primary-soft);
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.card .icon {
width: 40px; height: 40px;
display: grid; place-items: center;
border-radius: var(--radius-md);
background: var(--primary-bg);
color: var(--primary);
margin-bottom: 18px;
}
.card .icon svg { width: 20px; height: 20px; stroke-width: 1.6; }
.card h3 {
font-size: 17px;
margin: 0 0 8px;
font-weight: 600;
letter-spacing: -0.01em;
}
.card p {
margin: 0;
color: var(--muted-foreground);
font-size: 14.5px;
line-height: 1.55;
}
/* ===================== Step ===================== */
.step .num {
font-family: var(--font-mono);
font-size: 44px;
font-weight: 600;
color: var(--primary);
line-height: 1;
margin-bottom: 16px;
}
.step h3 {
font-size: 16px;
font-weight: 600;
margin: 0 0 14px;
}
.cmd {
font-family: var(--font-mono);
font-size: 13px;
background: var(--muted);
color: var(--foreground);
padding: 8px 12px;
border-radius: var(--radius-xs);
display: block;
white-space: nowrap;
overflow-x: auto;
max-width: 100%;
}
.cmd::-webkit-scrollbar { height: 0; }
/* ===================== App card ===================== */
.app-card h3 {
font-size: 15.5px;
font-weight: 600;
margin: 0 0 14px;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.tag {
font-family: var(--font-mono);
font-size: 11.5px;
background: var(--muted);
color: var(--muted-foreground);
padding: 3px 8px;
border-radius: var(--radius-xs);
}
/* ===================== Pipeline ===================== */
.pipeline-wrap {
max-width: 960px;
margin: 0 auto;
}
.pipeline {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
position: relative;
padding: 40px 0 32px;
}
.pipeline::before {
content: "";
position: absolute;
top: 47px;
left: 7%;
right: 7%;
height: 2px;
background: linear-gradient(to right,
var(--primary-soft),
var(--primary) 15%,
var(--primary) 85%,
var(--primary-soft));
z-index: 0;
opacity: 0.55;
}
.stage {
flex: 1;
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.stage .dot {
width: 14px; height: 14px;
border-radius: 50%;
background: var(--primary);
margin: 0 0 18px;
box-shadow:
0 0 0 4px var(--background),
0 0 0 6px var(--primary-soft);
}
.stage .num-sm {
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted-foreground);
margin-bottom: 2px;
letter-spacing: 0.05em;
}
.stage .label {
font-size: 14px;
color: var(--foreground);
font-weight: 500;
}
@media (max-width: 767px) {
.pipeline {
flex-direction: column;
gap: 8px;
padding-left: 20px;
}
.pipeline::before {
top: 40px;
bottom: 40px;
left: 26px;
right: auto;
width: 2px;
height: auto;
background: linear-gradient(to bottom,
var(--primary-soft),
var(--primary) 15%,
var(--primary) 85%,
var(--primary-soft));
}
.stage {
flex-direction: row;
align-items: center;
gap: 14px;
text-align: left;
flex: none;
padding: 8px 0;
}
.stage .dot { margin: 0; flex-shrink: 0; }
.stage-meta { display: flex; flex-direction: column; }
}
/* ===================== FAQ ===================== */
.faq {
max-width: 760px;
margin: 0 auto;
}
.faq details {
border-bottom: 1px solid var(--border);
padding: 20px 4px;
}
.faq details:first-child { border-top: 1px solid var(--border); }
.faq details[open] summary { color: var(--primary); }
.faq summary {
list-style: none;
cursor: pointer;
font-weight: 600;
font-size: 16px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
transition: color var(--transition);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
content: "+";
font-family: var(--font-mono);
font-size: 22px;
color: var(--primary);
line-height: 1;
transition: transform var(--transition);
flex-shrink: 0;
}
.faq details[open] summary::after {
content: "";
}
.faq .answer {
color: var(--muted-foreground);
margin-top: 14px;
font-size: 15px;
line-height: 1.65;
}
/* ===================== Footer ===================== */
.site-footer {
border-top: 1px solid var(--border);
padding: 56px 0 32px;
}
.footer-grid {
display: grid;
gap: 32px;
grid-template-columns: 1.5fr 1fr 1fr 1fr;
}
@media (max-width: 767px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 479px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-brand p {
color: var(--muted-foreground);
font-size: 14px;
margin: 14px 0 0;
line-height: 1.65;
max-width: 22rem;
}
.footer-col h4 {
font-size: 12px;
font-weight: 600;
margin: 0 0 16px;
color: var(--foreground);
text-transform: uppercase;
letter-spacing: 0.1em;
font-family: var(--font-mono);
}
.footer-col ul {
list-style: none;
padding: 0; margin: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.footer-col a {
color: var(--muted-foreground);
font-size: 14px;
transition: color var(--transition);
}
.footer-col a:hover { color: var(--primary); }
.copyright {
text-align: center;
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border);
font-size: 13px;
color: var(--neutral-400);
font-family: var(--font-mono);
}
/* ===================== Focus ring ===================== */
:is(a, button, summary, .btn):focus-visible {
outline: 2px solid var(--ring);
outline-offset: 3px;
border-radius: 10px;
}
/* ===================== Fade-up ===================== */
.fade-up {
opacity: 0;
transform: translateY(14px);
transition:
opacity 520ms cubic-bezier(0.4, 0, 0.2, 1),
transform 520ms cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-up.visible {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.fade-up { opacity: 1; transform: none; transition: none; }
html { scroll-behavior: auto; }
}
/* ===================== Mobile tweaks ===================== */
@media (max-width: 639px) {
.hero { padding: 56px 0 48px; }
.section { padding: 64px 0; }
.section-head { margin-bottom: 40px; }
}
</style>
</head>
<body>
<!-- ============ Header ============ -->
<header class="site-header">
<div class="container">
<a href="#top" class="brand" aria-label="dotlog 首頁">
<span class="brand-dot" aria-hidden="true"></span>
<span>dotlog</span>
</a>
<nav class="nav" aria-label="主要導覽">
<a href="#features">功能特性</a>
<a href="#steps">文件</a>
<a href="#faq">FAQ</a>
<button class="theme-toggle" type="button" id="themeToggle" aria-label="切換深淺主題">
<svg class="moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
<svg class="sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="4"/>
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
</svg>
</button>
<a href="#" class="btn btn-primary nav-github">在 GitHub 查看</a>
</nav>
</div>
</header>
<main id="top">
<!-- ============ Hero ============ -->
<section class="hero">
<div class="container">
<h1 class="fade-up">
把所有日誌管線<br/>
<span class="accent">串成一行</span>
</h1>
<p class="subtitle fade-up">
dotlog 是為工程師而生的 CLI 工具。一條指令完成擷取、過濾、聚合、輸出,取代雜亂的 shell pipeline 與多套 daemon。
</p>
<div class="hero-badges fade-up">
<span>⭐ 1.2k Stars</span>
<span class="sep">·</span>
<span>98% 測試通過</span>
<span class="sep">·</span>
<span>MIT 授權</span>
</div>
<div class="hero-ctas fade-up">
<a href="#steps" class="btn btn-primary">立即開始</a>
<a href="#" class="btn btn-outline">在 GitHub 查看</a>
</div>
</div>
</section>
<!-- ============ Why dotlog ============ -->
<section class="section" id="features">
<div class="container">
<div class="section-head fade-up">
<div class="section-kicker">Why dotlog</div>
<h2>為什麼選擇 dotlog</h2>
<p class="section-sub">不需要 YAML、不需要 daemon、不需要額外的容器。一支二進位檔裝了就能用。</p>
</div>
<div class="grid-4">
<article class="card fade-up">
<div class="icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</div>
<h3>零配置啟動</h3>
<p>裝了就跑,不需要設定檔。<code>dotlog tap nginx</code> 一行搞定。</p>
</article>
<article class="card fade-up">
<div class="icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 10h18"/><path d="M8 14h2M8 17h5"/></svg>
</div>
<h3>全格式支援</h3>
<p>JSON、syslog、Apache、Nginx、K8s events 自動偵測並結構化。</p>
</article>
<article class="card fade-up">
<div class="icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg>
</div>
<h3>低資源佔用</h3>
<p>Rust 撰寫,單一二進位檔 6MB常駐記憶體不到 15MB。</p>
</article>
<article class="card fade-up">
<div class="icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/><polyline points="7 11 12 16 17 11"/><line x1="12" y1="4" x2="12" y2="16"/></svg>
</div>
<h3>原生輸出整合</h3>
<p>直接送進 S3、Loki、OpenTelemetry、Kafka無須轉接器。</p>
</article>
</div>
</div>
</section>
<!-- ============ Quick Start ============ -->
<section class="section" id="steps">
<div class="container">
<div class="section-head fade-up">
<div class="section-kicker">Quick Start</div>
<h2>5 分鐘上手</h2>
<p class="section-sub">四個指令,把你的伺服器日誌送到雲端。</p>
</div>
<div class="grid-steps">
<article class="step card fade-up">
<div class="num">01</div>
<h3>安裝</h3>
<code class="cmd">brew install dotlog</code>
</article>
<article class="step card fade-up">
<div class="num">02</div>
<h3>初始化</h3>
<code class="cmd">dotlog init</code>
</article>
<article class="step card fade-up">
<div class="num">03</div>
<h3>指定來源</h3>
<code class="cmd">dotlog tap nginx</code>
</article>
<article class="step card fade-up">
<div class="num">04</div>
<h3>串接輸出</h3>
<code class="cmd">dotlog pipe → s3</code>
</article>
</div>
</div>
</section>
<!-- ============ Integrations ============ -->
<section class="section">
<div class="container">
<div class="section-head fade-up">
<div class="section-kicker">Integrations</div>
<h2>你正在用的工具,它都支援</h2>
<p class="section-sub">開箱即用 60+ 種資料來源與 20+ 種輸出目標。</p>
</div>
<div class="grid-apps">
<article class="card app-card fade-up">
<h3>Web 伺服器</h3>
<div class="tag-list">
<span class="tag">nginx</span><span class="tag">apache</span><span class="tag">caddy</span><span class="tag">traefik</span>
</div>
</article>
<article class="card app-card fade-up">
<h3>容器</h3>
<div class="tag-list">
<span class="tag">docker</span><span class="tag">kubernetes</span><span class="tag">containerd</span><span class="tag">podman</span>
</div>
</article>
<article class="card app-card fade-up">
<h3>資料庫</h3>
<div class="tag-list">
<span class="tag">postgres</span><span class="tag">mysql</span><span class="tag">redis</span><span class="tag">mongo</span>
</div>
</article>
<article class="card app-card fade-up">
<h3>雲端服務</h3>
<div class="tag-list">
<span class="tag">aws-s3</span><span class="tag">gcs</span><span class="tag">cloudwatch</span><span class="tag">azure-logs</span>
</div>
</article>
<article class="card app-card fade-up">
<h3>訊息佇列</h3>
<div class="tag-list">
<span class="tag">kafka</span><span class="tag">nats</span><span class="tag">rabbitmq</span><span class="tag">sqs</span>
</div>
</article>
<article class="card app-card fade-up">
<h3>監控</h3>
<div class="tag-list">
<span class="tag">loki</span><span class="tag">datadog</span><span class="tag">otel</span><span class="tag">prometheus</span>
</div>
</article>
</div>
</div>
</section>
<!-- ============ Pipeline ============ -->
<section class="section">
<div class="container">
<div class="section-head fade-up">
<div class="section-kicker">Core Pipeline</div>
<h2>七個階段的完整流水線</h2>
<p class="section-sub">每一階段都能被單獨替換、停用或擴充。</p>
</div>
<div class="pipeline-wrap fade-up">
<div class="pipeline" role="list">
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">01</span>
<span class="label">擷取</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">02</span>
<span class="label">解析</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">03</span>
<span class="label">正規化</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">04</span>
<span class="label">路由</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">05</span>
<span class="label">過濾</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">06</span>
<span class="label">聚合</span>
</div>
</div>
<div class="stage" role="listitem">
<div class="dot" aria-hidden="true"></div>
<div class="stage-meta">
<span class="num-sm">07</span>
<span class="label">輸出</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============ FAQ ============ -->
<section class="section" id="faq">
<div class="container">
<div class="section-head fade-up">
<div class="section-kicker">FAQ</div>
<h2>常見問題</h2>
</div>
<div class="faq fade-up">
<details>
<summary>dotlog 的授權是什麼?</summary>
<div class="answer">採用 MIT 授權,商用、修改、再散布都沒有限制,無需付費。</div>
</details>
<details>
<summary>效能表現如何?</summary>
<div class="answer">在單一 vCPU 上可處理約 120k events/秒,約為 Fluentd 的 4 倍,記憶體佔用不到 1/5。</div>
</details>
<details>
<summary>與 Fluentd / Vector / Filebeat 有什麼差異?</summary>
<div class="answer">dotlog 強調 <strong>CLI 優先與零配置</strong>:用熟悉的 shell 就能組裝管線,無需學習自訂 DSL也不需要部署守護程序。適合需要快速串接或臨時任務的場景。</div>
</details>
<details>
<summary>支援哪些部署方式?</summary>
<div class="answer">單一靜態二進位檔,直接在 macOS、Linux、Windows 執行;也提供官方 Docker image 與 Helm chart。</div>
</details>
<details>
<summary>資料會經過你們的伺服器嗎?</summary>
<div class="answer">完全不會。dotlog 是純本地執行的工具,所有日誌只在你指定的來源與輸出之間傳遞,專案本身不收集任何遙測資料(可用 <code>--telemetry=off</code> 再次關閉驗證)。</div>
</details>
</div>
</div>
</section>
</main>
<!-- ============ Footer ============ -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<div class="brand">
<span class="brand-dot" aria-hidden="true"></span>
<span>dotlog</span>
</div>
<p>把所有日誌管線串成一行。為工程師而生的開源 CLI 工具。</p>
</div>
<div class="footer-col">
<h4>產品</h4>
<ul>
<li><a href="#features">功能特性</a></li>
<li><a href="#steps">快速上手</a></li>
<li><a href="#">更新日誌</a></li>
</ul>
</div>
<div class="footer-col">
<h4>資源</h4>
<ul>
<li><a href="#">文件</a></li>
<li><a href="#">GitHub</a></li>
<li><a href="#">社群討論</a></li>
</ul>
</div>
<div class="footer-col">
<h4>法律</h4>
<ul>
<li><a href="#">授權條款</a></li>
<li><a href="#">隱私政策</a></li>
</ul>
</div>
</div>
<div class="copyright">© 2026 dotlog · MIT License</div>
</div>
</footer>
<script>
// ==== Theme toggle ====
(function () {
const root = document.documentElement;
const toggle = document.getElementById('themeToggle');
const stored = localStorage.getItem('dotlog-theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
root.classList.add('dark');
}
toggle.addEventListener('click', () => {
root.classList.toggle('dark');
localStorage.setItem('dotlog-theme', root.classList.contains('dark') ? 'dark' : 'light');
});
})();
// ==== Fade-up on scroll ====
(function () {
const items = document.querySelectorAll('.fade-up');
if (!('IntersectionObserver' in window)) {
items.forEach((el) => el.classList.add('visible'));
return;
}
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add('visible');
io.unobserve(e.target);
}
});
}, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' });
items.forEach((el) => io.observe(el));
})();
</script>
</body>
</html>

BIN
screenshot-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

BIN
screenshot-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB