Color mode

Stable CSS Hooks と Cascade

このページは テーマ作成の詳細 の一部で、Stable CSS Hook、Character Layer、userCss、CSS Cascade を扱います。

20. Stable CSS Hooks

Theme は Application や Plugin の内部 Markup ではなく、公開された Stable CSS Hook を対象にします。

Hook には大きく2つの Namespace があります。

Namespace 所有者 用途
rb-* Framework Site の構造
rr-* Plugin / Feature Plugin UI

Framework が提供する代表的な Hook は、

text
.rb-theme-root
.rb-site
.rb-article
.rb-article-layout
.rb-article-header
.rb-article-body
.rb-article-content
.rb-article-meta
.rb-article-footer
.rb-sidebar

です。

レンダリングされた Markdown 本文は .rb-article-content に包まれ、Markdown のセマンティックなベースライン(リストマーカーと字下げ、見出し、段落とブロックの余白、表、図、定義リスト、インラインコード、整形済みブロック)はこの wrapper が持ちます。.rb-article-body は header、metadata、本文、plugin slot を含む article body のシェルです。ベースラインはレイヤー化されているため、テーマは構造を再宣言する必要はなく、--rb-* トークンとレイヤー外のキャラクター規則で見た目を表現します。

Plugin は、

text
.rr-search
.rr-callout
.rr-table-of-contents
.rr-backlinks
.rr-local-graph
.rr-code
.rr-code-tabs
.rr-lightbox
.rr-excalidraw
.rr-mermaid
.rr-query
.rr-cardlink
.rr-diff-history
.rr-attachment
.rr-media
.rr-recent-posts
.rr-garden-explorer

などの Root Hook を提供できます。

Theme はこれらの Stable Hook を対象にします。

21. Plugin の内部 Class

Plugin は内部で、

text
.rr-search
.rr-search__input
.rr-search__result
.rr-search--loading

のような BEM Class を使う場合があります。

基本的に Public Hook は、

text
.rr-search

です。

text
__input
__result
--loading

などは、Plugin が明示的に Public Hook として文書化していない限り内部実装として扱います。

.sr-only のような一般的な Helper Class も Plugin Hook ではありません。

後方互換性のため旧 Class と .rr-* が同じ要素に存在する場合でも、Theme は .rr-* を利用してください。

22. Character Layer

Theme は Token を変更するだけでなく、Stable Hook を直接 Style して視覚的な個性を与えられます。

たとえば、

css
:is(:root, .rb-theme-root)
[data-theme-name="example"]
.rb-article-header {
  border-bottom:
    var(--rb-rule-width)
    solid
    var(--rb-color-border);
}

のような変更です。

対象にできるのは Stable Hook です。

Theme 側で新しい、

text
rb-*
rr-*

Class を発明して Framework Contract のように扱わないでください。

Character Layer も Presentation 専用です。

Content、Structure、Behavior を変更してはいけません。

23. CSS Layer

Token Definition は @layer base に置きます。

css
@layer base {
  :is(:root, .rb-theme-root)
  [data-theme-name="example"] {
    --rb-color-accent: #b45309;
  }
}

一方、Stable Hook に対する Character Rule は unlayered にします。

css
:is(:root, .rb-theme-root)
[data-theme-name="example"]
.rb-article-header {
  border-bottom:
    var(--rb-rule-width)
    solid
    var(--rb-color-border);
}

Application の Structural CSS と Plugin CSS も unlayered です。

Theme CSS はそれらより後に読み込まれるため、通常は !important を使わなくても上書きできます。

!important に依存しないでください。

24. Web Font

Theme Package は Self-hosted Web Font を含めることができます。

たとえば、

text
styles/
├─ theme.css
└─ fonts/
   └─ example-serif-latin.woff2

のように配置します。

CSS では相対 URL を使います。

css
@font-face {
  font-family: "Example Serif";
 
  src:
    url("./fonts/example-serif-latin.woff2")
    format("woff2");
 
  font-weight: 400 700;
  font-display: swap;
}

Font を同梱する場合は、その Font の License File も Package に含めてください。

Latin Subset のような比較的小さい Font は同梱できます。

日本語などの CJK Font は File Size が大きいため、基本的には System Font Stack へ fallback します。

25. userCss

userCss は Site 利用者が Theme の上から最終調整するための CSS です。

ts
theme: defaultTheme({
  userCss: [
    "/extensions/custom.css",
  ],
}),

Theme の Stylesheet より後に読み込まれるため、userCss が最終的な Override になります。

Theme Package 側で userCss より強い Selector や !important を多用しないでください。

26. CSS Cascade

Riebeckite では CSS の読み込み順も Contract の一部です。

Diagram source
text
flowchart TD
    Base["Base / Application<br/>Structural CSS"]
    Plugin["Plugin Default CSS"]
    Theme["Theme CSS"]
    Token["Config Token<br/>Inline Style"]
    User["userCss"]
 
    Base --> Plugin
    Plugin --> Theme
    Theme --> Token
    Token --> User

順番は、

text
Framework Structural CSS
        ↓
Base / Application CSS
        ↓
Plugin Default CSS
        ↓
Theme CSS
        ↓
Config Token Inline Style
        ↓
userCss

です。

この順番は偶然ではなく、Presentation Extension の Contract として保証されます。

@riebeckite/honox は、

text
.riebeckite/framework-styles.css
.riebeckite/plugin-styles.css
.riebeckite/theme-styles.css

を生成します。

Site は Framework Stylesheet を Plugin Stylesheet より先に、Plugin Stylesheet を Theme Stylesheet より先に読み込みます。

そのため、

text
Plugin
  → 標準の見た目
 
Theme
  → Plugin の見た目を変更
 
userCss
  → Site 利用者が最終調整

という関係になります。

生成された Stylesheet を直接編集したり、Import 順を変更したりしないでください。

30. 見た目がおかしい場合

Theme が期待どおりに適用されない場合は、まず次の順番で確認します。

Diagram source
text
flowchart TD
    Start["Themeが適用されない"]
 
    Start --> Name{"data-theme-name は正しい?"}
    Name -->|No| FixName["Theme nameを確認"]
    Name -->|Yes| Hook{"正しいHookを対象にしている?"}
 
    Hook -->|No| FixHook["rb-* / rr-* を確認"]
    Hook -->|Yes| Cascade{"Cascadeは正しい?"}
 
    Cascade -->|No| FixCascade["Plugin → Theme → userCssを確認"]
    Cascade -->|Yes| Mode{"Color Mode条件は正しい?"}
 
    Mode -->|No| FixMode["data-theme / systemを確認"]
    Mode -->|Yes| CSS["Selector / CSSを確認"]

特に確認するのは、

  1. data-theme-name が Theme の name と一致しているか
  2. .rb-* / .rr-* の正しい Stable Hook を対象にしているか
  3. Plugin CSS → Theme CSS → userCss の順になっているか
  4. system なのに data-theme="" が残っていないか
  5. Theme Root の外へ Selector が漏れていないか

です。

History

1 changesCollapseExpand
1 + ---
2 + title: Stable CSS Hooks と Cascade
3 + sidebar:
4 + label: CSS Hooks と Cascade
5 + order: 50
6 + ---
7 + # Stable CSS Hooks と Cascade
8 +
9 + このページは [テーマ作成の詳細](../theme-system.ja.md) の一部で、Stable CSS Hook、Character Layer、`userCss`、CSS Cascade を扱います。
10 +
11 + ## 20. Stable CSS Hooks
12 +
13 + Theme は Application や Plugin の内部 Markup ではなく、公開された Stable CSS Hook を対象にします。
14 +
15 + Hook には大きく2つの Namespace があります。
16 +
17 + | Namespace | 所有者 | 用途 |
18 + | --- | --- | --- |
19 + | `rb-*` | Framework | Site の構造 |
20 + | `rr-*` | Plugin / Feature | Plugin UI |
21 +
22 + Framework が提供する代表的な Hook は、
23 +
24 + ```text id="etblkj"
25 + .rb-theme-root
26 + .rb-site
27 + .rb-article
28 + .rb-article-layout
29 + .rb-article-header
30 + .rb-article-body
31 + .rb-article-content
32 + .rb-article-meta
33 + .rb-article-footer
34 + .rb-sidebar
35 + ```
36 +
37 + です。
38 +
39 + レンダリングされた Markdown 本文は `.rb-article-content` に包まれ、Markdown のセマンティックなベースライン(リストマーカーと字下げ、見出し、段落とブロックの余白、表、図、定義リスト、インラインコード、整形済みブロック)はこの wrapper が持ちます。`.rb-article-body` は header、metadata、本文、plugin slot を含む article body のシェルです。ベースラインはレイヤー化されているため、テーマは構造を再宣言する必要はなく、`--rb-*` トークンとレイヤー外のキャラクター規則で見た目を表現します。
40 +
41 + Plugin は、
42 +
43 + ```text id="rt44pe"
44 + .rr-search
45 + .rr-callout
46 + .rr-table-of-contents
47 + .rr-backlinks
48 + .rr-local-graph
49 + .rr-code
50 + .rr-code-tabs
51 + .rr-lightbox
52 + .rr-excalidraw
53 + .rr-mermaid
54 + .rr-query
55 + .rr-cardlink
56 + .rr-diff-history
57 + .rr-attachment
58 + .rr-media
59 + .rr-recent-posts
60 + .rr-garden-explorer
61 + ```
62 +
63 + などの Root Hook を提供できます。
64 +
65 + Theme はこれらの Stable Hook を対象にします。
66 +
67 +
68 + ## 21. Plugin の内部 Class
69 +
70 + Plugin は内部で、
71 +
72 + ```text id="u7bmkr"
73 + .rr-search
74 + .rr-search__input
75 + .rr-search__result
76 + .rr-search--loading
77 + ```
78 +
79 + のような BEM Class を使う場合があります。
80 +
81 + 基本的に Public Hook は、
82 +
83 + ```text id="9k6dgy"
84 + .rr-search
85 + ```
86 +
87 + です。
88 +
89 + ```text id="1ggxg1"
90 + __input
91 + __result
92 + --loading
93 + ```
94 +
95 + などは、Plugin が明示的に Public Hook として文書化していない限り内部実装として扱います。
96 +
97 + `.sr-only` のような一般的な Helper Class も Plugin Hook ではありません。
98 +
99 + 後方互換性のため旧 Class と `.rr-*` が同じ要素に存在する場合でも、Theme は `.rr-*` を利用してください。
100 +
101 +
102 + ## 22. Character Layer
103 +
104 + Theme は Token を変更するだけでなく、Stable Hook を直接 Style して視覚的な個性を与えられます。
105 +
106 + たとえば、
107 +
108 + ```css id="n72uhc"
109 + :is(:root, .rb-theme-root)
110 + [data-theme-name="example"]
111 + .rb-article-header {
112 + border-bottom:
113 + var(--rb-rule-width)
114 + solid
115 + var(--rb-color-border);
116 + }
117 + ```
118 +
119 + のような変更です。
120 +
121 + 対象にできるのは Stable Hook です。
122 +
123 + Theme 側で新しい、
124 +
125 + ```text id="mkmfyh"
126 + rb-*
127 + rr-*
128 + ```
129 +
130 + Class を発明して Framework Contract のように扱わないでください。
131 +
132 + Character Layer も Presentation 専用です。
133 +
134 + Content、Structure、Behavior を変更してはいけません。
135 +
136 +
137 + ## 23. CSS Layer
138 +
139 + Token Definition は `@layer base` に置きます。
140 +
141 + ```css id="43rbsh"
142 + @layer base {
143 + :is(:root, .rb-theme-root)
144 + [data-theme-name="example"] {
145 + --rb-color-accent: #b45309;
146 + }
147 + }
148 + ```
149 +
150 + 一方、Stable Hook に対する Character Rule は **unlayered** にします。
151 +
152 + ```css id="s2xwqm"
153 + :is(:root, .rb-theme-root)
154 + [data-theme-name="example"]
155 + .rb-article-header {
156 + border-bottom:
157 + var(--rb-rule-width)
158 + solid
159 + var(--rb-color-border);
160 + }
161 + ```
162 +
163 + Application の Structural CSS と Plugin CSS も unlayered です。
164 +
165 + Theme CSS はそれらより後に読み込まれるため、通常は `!important` を使わなくても上書きできます。
166 +
167 + `!important` に依存しないでください。
168 +
169 +
170 + ## 24. Web Font
171 +
172 + Theme Package は Self-hosted Web Font を含めることができます。
173 +
174 + たとえば、
175 +
176 + ```text id="8g43c5"
177 + styles/
178 + ├─ theme.css
179 + └─ fonts/
180 + └─ example-serif-latin.woff2
181 + ```
182 +
183 + のように配置します。
184 +
185 + CSS では相対 URL を使います。
186 +
187 + ```css id="66ktv6"
188 + @font-face {
189 + font-family: "Example Serif";
190 +
191 + src:
192 + url("./fonts/example-serif-latin.woff2")
193 + format("woff2");
194 +
195 + font-weight: 400 700;
196 + font-display: swap;
197 + }
198 + ```
199 +
200 + Font を同梱する場合は、その Font の License File も Package に含めてください。
201 +
202 + Latin Subset のような比較的小さい Font は同梱できます。
203 +
204 + 日本語などの CJK Font は File Size が大きいため、基本的には System Font Stack へ fallback します。
205 +
206 +
207 + ## 25. `userCss`
208 +
209 + `userCss` は Site 利用者が Theme の上から最終調整するための CSS です。
210 +
211 + ```ts id="mphtk3"
212 + theme: defaultTheme({
213 + userCss: [
214 + "/extensions/custom.css",
215 + ],
216 + }),
217 + ```
218 +
219 + Theme の Stylesheet より後に読み込まれるため、`userCss` が最終的な Override になります。
220 +
221 + Theme Package 側で `userCss` より強い Selector や `!important` を多用しないでください。
222 +
223 +
224 + ## 26. CSS Cascade
225 +
226 + Riebeckite では CSS の読み込み順も Contract の一部です。
227 +
228 + ```mermaid id="e5j94x"
229 + flowchart TD
230 + Base["Base / Application<br/>Structural CSS"]
231 + Plugin["Plugin Default CSS"]
232 + Theme["Theme CSS"]
233 + Token["Config Token<br/>Inline Style"]
234 + User["userCss"]
235 +
236 + Base --> Plugin
237 + Plugin --> Theme
238 + Theme --> Token
239 + Token --> User
240 + ```
241 +
242 + 順番は、
243 +
244 + ```text id="jz92ku"
245 + Framework Structural CSS
246 + ↓
247 + Base / Application CSS
248 + ↓
249 + Plugin Default CSS
250 + ↓
251 + Theme CSS
252 + ↓
253 + Config Token Inline Style
254 + ↓
255 + userCss
256 + ```
257 +
258 + です。
259 +
260 + この順番は偶然ではなく、Presentation Extension の Contract として保証されます。
261 +
262 + `@riebeckite/honox` は、
263 +
264 + ```text id="i2y97j"
265 + .riebeckite/framework-styles.css
266 + .riebeckite/plugin-styles.css
267 + .riebeckite/theme-styles.css
268 + ```
269 +
270 + を生成します。
271 +
272 + Site は Framework Stylesheet を Plugin Stylesheet より先に、Plugin Stylesheet を Theme Stylesheet より先に読み込みます。
273 +
274 + そのため、
275 +
276 + ```text id="4w8d9d"
277 + Plugin
278 + → 標準の見た目
279 +
280 + Theme
281 + → Plugin の見た目を変更
282 +
283 + userCss
284 + → Site 利用者が最終調整
285 + ```
286 +
287 + という関係になります。
288 +
289 + 生成された Stylesheet を直接編集したり、Import 順を変更したりしないでください。
290 +
291 +
292 + ## 30. 見た目がおかしい場合
293 +
294 + Theme が期待どおりに適用されない場合は、まず次の順番で確認します。
295 +
296 + ```mermaid id="pm3ukv"
297 + flowchart TD
298 + Start["Themeが適用されない"]
299 +
300 + Start --> Name{"data-theme-name は正しい?"}
301 + Name -->|No| FixName["Theme nameを確認"]
302 + Name -->|Yes| Hook{"正しいHookを対象にしている?"}
303 +
304 + Hook -->|No| FixHook["rb-* / rr-* を確認"]
305 + Hook -->|Yes| Cascade{"Cascadeは正しい?"}
306 +
307 + Cascade -->|No| FixCascade["Plugin → Theme → userCssを確認"]
308 + Cascade -->|Yes| Mode{"Color Mode条件は正しい?"}
309 +
310 + Mode -->|No| FixMode["data-theme / systemを確認"]
311 + Mode -->|Yes| CSS["Selector / CSSを確認"]
312 + ```
313 +
314 + 特に確認するのは、
315 +
316 + 1. `data-theme-name` が Theme の `name` と一致しているか
317 + 2. `.rb-*` / `.rr-*` の正しい Stable Hook を対象にしているか
318 + 3. Plugin CSS → Theme CSS → `userCss` の順になっているか
319 + 4. `system` なのに `data-theme=""` が残っていないか
320 + 5. Theme Root の外へ Selector が漏れていないか
321 +
322 + です。
323 +