OpenPress

@open-press/core/theme

Theme API

定義 slide / fixed-layout workspace 的可移植 theme token:colors、fonts、typography、CSS variables 與 theme specimen components。

@open-press/core/theme 是 OpenPress 公開的 theme token API。用它替單一 Press 定義 color、font、typography roles,再把結果傳給 <Press theme={...}>

Theme object 和 press/<slug>/theme/ 是兩層不同責任:

  • Theme object 定義 Press profile(documentslidebare),把 theme metadata 寫進 reader / Workbench,並在量測與 runtime 注入 --op-theme-* CSS variables。
  • press/<slug>/theme/ 仍然是實際 CSS source,放字型載入、prose rules、slide template classes 與專案自己的 selectors。
  • 頁面尺寸仍然由 <Press page> 負責。不要把 page geometry 寫進 theme object。
Function Impl

# defineTheme

將可讀的 theme input 正規化為可消費的 token graph,包含標準 roles、擴充 tokens、resolved colors、typography 與 CSS variables。

import { defineTheme, defineSlideTheme, defineDocumentTheme } from "@open-press/core/theme";
const sourceTheme = defineTheme({
profile: "slide",
name: "Source Deck",
colors: {
  bg: "#f8f2e6",
  ink: "#111217",
  accent: { value: "#d7332f", label: "Red rule" },
  marker: "#d7332f",
},
fonts: {
  serif: "Georgia, 'Times New Roman', serif",
  sans: "Inter, system-ui, sans-serif",
},
typography: {
  title: { font: "serif", size: 124, lineHeight: 1.02, weight: 400, color: "ink" },
  body: { font: "sans", size: 40, lineHeight: 1.35, color: "ink" },
  eyebrow: { font: "sans", size: 14, lineHeight: 1.1, weight: 800, tracking: "0.16em", color: "marker", transform: "uppercase" },
},
extend: {
  colors: {
    brandGlow: "#ffee88",
  },
  typography: {
    tinyNote: { font: "sans", size: 10, lineHeight: 1.2, color: "muted" },
  },
},
});

Input

Name Type Default Description
name string Theme 名稱,供 specimen、Workbench 或文件顯示。
description string Theme 說明。
profile "slide" | "document" | "bare" 選擇內建標準 token 組。預設是 `slide`。只有低階 token registry 才建議用 `bare`。
colors Record<string, ThemeColorInput> 覆寫標準色票 roles。可傳 hex/string,或 `{ value, label, description, foreground }`。
fonts Record<string, string> 字體族群 token,例如 `serif`、`sans`、`mono`。
typography Record<string, ThemeTypographyInput> 覆寫標準文字 roles,包含 `font`、`size`、`lineHeight`、`weight`、`tracking`、`color`、`transform`、`sample`。
extend { colors?, fonts?, typography? } 新增專案專屬 token,避免和內建標準 roles 混在一起。

建議設定方式

Page-based Press 使用 document theme:

import { Press } from "@open-press/core";
import { mdxSource } from "@open-press/core/mdx";
import { defineDocumentTheme } from "@open-press/core/theme";

const documentTheme = defineDocumentTheme({
  name: "Research Report",
  colors: {
    paper: "#ffffff",
    ink: "#16161d",
    accent: "#0d5f89",
  },
  fonts: {
    body: '"IBM Plex Sans", system-ui, sans-serif',
    serif: '"Noto Serif", Georgia, serif',
    mono: '"SFMono-Regular", Menlo, monospace',
  },
  typography: {
    heading: { font: "serif", size: "28pt", lineHeight: 1.18, weight: 500, color: "ink" },
    body: { font: "body", size: "10.5pt", lineHeight: 1.7, color: "ink" },
    caption: { font: "body", size: "8.5pt", lineHeight: 1.4, color: "muted" },
  },
});

export default function ReportPress() {
  return (
    <Press
      slug="report"
      title="Research Report"
      page="a4"
      theme={documentTheme}
      sources={[mdxSource({ id: "story", preset: "section-folders", root: "report/chapters" })]}
    >
      {/* Frames, Toc, Sections */}
    </Press>
  );
}

Slide Press 使用 slide theme:

import { Press, Slide } from "@open-press/core";
import { defineSlideTheme } from "@open-press/core/theme";

const slideTheme = defineSlideTheme({
  name: "Source Deck",
  colors: {
    bg: "#fcf7e9",
    ink: "#16161d",
    accent: "#d42a20",
    marker: "#d42a20",
  },
  typography: {
    display: { font: "serif", size: 138, lineHeight: 1.05, color: "ink" },
    title: { font: "serif", size: 72, lineHeight: 1.12, color: "ink" },
    body: { font: "sans", size: 36, lineHeight: 1.48, color: "muted" },
  },
});

export default function DeckPress() {
  return (
    <Press slug="deck" title="Source Deck" type="slides" page="slide-16-9" theme={slideTheme}>
      <Slide id="cover" />
      <Slide id="agenda" />
    </Press>
  );
}

<Press type="slides"> 可接受 defineSlideTheme()bare theme object。Page-based Press 可接受 defineDocumentTheme()bare theme object。把 document theme 傳給 slide Press,或把 slide theme 傳給 page Press,export 時會報錯。

Standard profiles

一般 authoring 優先使用 profile helper:

const slideTheme = defineSlideTheme({
  colors: { marker: "#df4b21" },
  typography: { eyebrow: { size: 14, lineHeight: 1, weight: 800, color: "marker" } },
});

const documentTheme = defineDocumentTheme({
  colors: { accent: "#0d5f89" },
});

Slide theme 內建 color roles:bgsurfacesurfaceMutedinkmutedlineaccentquotesuccesswarningdangermarker

Slide theme 內建 typography roles:displaytitlesectionleadbodycaptioneyebrowmarkerquotemono

Document theme 內建 color roles:bgpapersurfacesurfaceMutedinkmutedlineaccentlinkquotemarkerannotation

Document theme 內建 typography roles:titleheadingsubheadingbodybodyStrongcaptionfootnotepageNumbereyebrowmarkermono

專案專屬 token 例如 brandGlowdataPositivelegalFinePrint 應放在 extend。新增命名前,先優先覆寫標準 roles。

CSS variables

defineTheme 會輸出穩定 CSS variables。當 theme object 傳給 <Press theme={...}> 後,exporter 會把這些 variables 注入 measurement CSS 與 reader runtime:

  • colors: --op-theme-color-<token>
  • typography font family: --op-theme-type-<token>-font-family
  • typography size: --op-theme-type-<token>-font-size
  • typography line height: --op-theme-type-<token>-line-height
  • typography weight: --op-theme-type-<token>-font-weight
  • typography tracking: --op-theme-type-<token>-letter-spacing
  • typography color: --op-theme-type-<token>-color

Template CSS 應該消費這些 variables:

.op-source-deck-title {
  font-family: var(--op-theme-type-title-font-family);
  font-size: var(--op-theme-type-title-font-size);
  line-height: var(--op-theme-type-title-line-height);
  color: var(--op-theme-type-title-color);
}

.op-source-deck-red-rule {
  background: var(--op-theme-color-accent);
}

專案自己的 selector CSS 應放在 press/<slug>/theme/,並從那裡讀取 theme variables:

.report-prose h2 {
  font-family: var(--op-theme-type-heading-font-family);
  font-size: var(--op-theme-type-heading-font-size);
  line-height: var(--op-theme-type-heading-line-height);
}

當 Press 需要指向非預設 theme 目錄時,可以使用 path 寫法:

<Press theme="./theme" />

只有當你要把某個 Press 指到非預設 theme 目錄時,才使用 path 寫法。新工作建議使用 typed theme object,並保留 folder-local 的 press/<slug>/theme/ CSS 檔。

Function Impl

# themeToCssVariables

回傳可直接放入 React `style` 的 CSS variable map。

import { themeToCssVariables } from "@open-press/core/theme";
const style = themeToCssVariables(sourceTheme);
// -> { "--op-theme-color-bg": "#f8f2e6", ... }
Function Impl

# themeToCssText

輸出 CSS text,適合寫入 theme stylesheet 或產生 theme preview。

import { themeToCssText } from "@open-press/core/theme";
const css = themeToCssText(sourceTheme, ".op-source-deck-slide");

Specimen components

Component Impl

# ThemeColorSwatches

渲染 theme color swatches,適合放在 template 的 theme-colors slide。

import { ThemeColorSwatches } from "@open-press/core/theme";
<ThemeColorSwatches theme={sourceTheme} />
Component Impl

# ThemeTypographyGraph

渲染 typography token graph,適合檢查簡報字級、字體、行高與顏色。

import { ThemeTypographyGraph } from "@open-press/core/theme";
<ThemeTypographyGraph theme={sourceTheme} sample="A line is length without breadth." />
Component Impl

# ThemeSpec

一次渲染 color 與 typography specimen。

import { ThemeSpec } from "@open-press/core/theme";
<ThemeSpec theme={sourceTheme} sections={["colors", "typography"]} />

Types

Types: OpenPressThemeInput, ThemeProfile, ThemeExtensionInput, ThemeColorInput, ThemeTypographyInput, SlideThemeColorRole, SlideThemeTypographyRole, DocumentThemeColorRole, DocumentThemeTypographyRole, DefinedOpenPressTheme, DefinedSlideTheme, DefinedDocumentTheme, DefinedBareTheme, DefinedThemeColor, DefinedThemeTypography, ThemeVisualizationProps

Theme API 只規範 token,不規範 layout。Slide 位置仍然由 Frame / Text / Line / MediaObjectboxFrame layout 表達。