
Neovimのカラーテーマ「sakurajima.nvim」を作成して学んだこと
- 日本語
- Neovim
- Color Theme
はじめに
2024年5月、私は自作のNeovimカラーテーマ「sakurajima.nvim」を作成した。
そして2年間、毎日このテーマを使い続けた。
「いつか直そう」と思いながら。
2年である。730日。17,520時間。その間、私は毎日「ここの色、ちょっと見づらいな…」と思いながらコードを書いていた。人間の適応能力は恐ろしい。
そしてついに2026年1月、重い腰を上げてリファクタリングを実施した。この記事では、カラーテーマ作成で学んだことと、2年越しの大改修について書いていく。
sakurajima.nvimとは
鹿児島県にある活火山「桜島」をモチーフにしたNeovim用のダークテーマである。
溶岩のような暖色系と、火山灰をイメージしたグレーがかった背景が特徴だ。名前の由来は単純で、「かっこいい日本語の名前をつけたかった」というだけである。富士山は既に使われていそうだったので桜島にした。
リポジトリ: Daiki48/sakurajima.nvim
ディレクトリ構成
sakurajima.nvim/ ├── colors/sakurajima.lua # :colorscheme で呼び出される入口 ├── lua/ │ ├── lualine/themes/sakurajima.lua # lualine用テーマ │ └── sakurajima/ │ ├── init.lua # メインの初期化処理 │ ├── colors.lua # カラーパレット定義 │ ├── highlight.lua # ハイライト設定のユーティリティ │ ├── plugins/hop.lua # hop.nvim対応 │ └── themes/dinner.lua # 全ハイライトグループ定義 └── screenshots/
themes/dinner.luaという命名は、桜島の夕景をイメージしている。lunch.luaも用意してあるが、中身は空っぽだ。いつかライトテーマを作ろうと思って2年が経った。
まだ実装予定は無い
カラーパレットの設計思想
火山をイメージした配色
-- lua/sakurajima/colors.lua より抜粋 local colors = { -- 溶岩のような暖色 orange = "#E38D2C", dark_orange = "#97812C", red = "#E34C36", yellow = "#E3D92C", -- 火山灰をイメージした背景 black = "#22272e", winter_gray = "#2D333B", -- 冷たさを加えるアクセント cyan = "#2BB6BA", dark_cyan = "#3B7B7D", }
暖色を中心にしつつ、シアン系の色でアクセントを加えている。全部が暖色だと目が疲れるし、何より「火山の近くには海がある」という理由をつけてシアンを入れた。後付けの理由である。
セマンティックな色の割り当て
| 色 | 用途 |
|---|---|
dark_yellow | 関数名 |
dark_cyan | キーワード |
dark_orange | 文字列 |
dark_green | 定数・数値 |
light_cyan | 型 |
これらの割り当ては、他の有名テーマを参考にしつつ、自分が「この色はこの要素っぽい」と感じるものを選んだ。関数が黄色っぽいのは、なんとなく「動き」を感じるからだ。理論的根拠はない。
2年越しのリファクタリング
Before: 2024年の実装
-- 旧実装 function highlight.setup(group, guifg, guibg, gui, guisp) local cmd = "highlight " .. group if guifg then cmd = cmd .. " guifg=" .. guifg end if guibg then cmd = cmd .. " guibg=" .. guibg end -- ... vim.cmd(cmd) end
文字列を連結してvim.cmd()に渡すという、いかにも「Vimscriptから移行したばかり」な実装だった。動くには動くが、エレガントとは言い難い。
After: 2026年の実装
-- 新実装 function highlight.setup(group, opts) local hl_opts = {} for k, v in pairs(opts) do if v ~= nil and v ~= "NONE" and v ~= "cleared" then hl_opts[k] = v end end vim.api.nvim_set_hl(0, group, hl_opts) end
vim.api.nvim_set_hl()を使ったモダンな実装に変更した。Neovim 0.7で追加されたAPIだが、当時の私は知らなかったのか、知っていて使わなかったのか、もはや記憶にない。
呼び出し側も変わった。
-- Before highlight.setup("Function", colors.dark_yellow, nil, colors.bold, nil) -- After highlight.setup("Function", { fg = colors.dark_yellow, bold = true })
テーブルベースになったことで、何を設定しているのかが一目瞭然になった。nilの羅列を見て「これは何番目の引数だっけ」と考える必要がなくなった。
ハイライトリンクという知恵
カラーテーマを作る上で最も重要な学びは「ハイライトリンク」の活用だった。
-- 基本グループを定義 highlight.setup("Constant", { fg = colors.dark_green }) -- 関連グループをリンク highlight.link("Character", "Constant") highlight.link("Number", "Constant") highlight.link("Float", "Constant") highlight.link("Boolean", "Constant")
「定数」という概念に対して色を決めたら、その下位概念(文字、数値、浮動小数点、真偽値)は全てリンクで済ませる。これにより:
- 色の変更が一箇所で済む
- テーマ全体の統一感が保たれる
- 新しいハイライトグループが増えても対応しやすい
最初は全部個別に定義していたが、途中で「これは無理がある」と気づいてリンク方式に切り替えた。
Tree-sitter対応の苦労
Neovimの構文ハイライトは、従来のVim方式からTree-sitter方式に移行しつつある。つまり、両方に対応する必要がある。
-- 従来のVim構文グループ highlight.setup("Function", { fg = colors.dark_yellow }) -- Tree-sitterのキャプチャグループ highlight.setup("@function", { fg = colors.dark_yellow }) highlight.setup("@function.builtin", { fg = colors.dark_yellow }) highlight.setup("@function.call", { fg = colors.dark_yellow }) highlight.setup("@function.method", { fg = colors.dark_yellow })
Tree-sitterのキャプチャグループは細かく分かれているので、定義数が膨れ上がる。しかしこの細かさのおかげで、「組み込み関数だけ色を変える」といった細やかな調整ができる。
今回は全部同じ色にしたが。
プラグイン対応
lualine.nvim
ステータスラインプラグインのlualine.nvimには専用テーマを用意した。
-- lua/lualine/themes/sakurajima.lua local sakurajima = { normal = { a = { bg = colors.dark_gray, fg = colors.black, gui = "bold" }, b = { bg = colors.winter_gray, fg = colors.light_gray }, c = { bg = colors.winter_gray, fg = colors.light_gray }, }, insert = { a = { bg = colors.dark_blue, fg = colors.black, gui = "bold" }, }, -- ... }
モードごとに色を変えることで、今どのモードにいるかが一目でわかる。Vimmerにとってモードの把握は死活問題だ。
hop.nvim
画面内をジャンプするhop.nvimにも対応した。
highlight.setup("HopNextKey", { fg = colors.cyan, bold = true }) highlight.setup("HopNextKey1", { fg = colors.green, bold = true }) highlight.setup("HopNextKey2", { fg = colors.red, bold = true })
ジャンプ先のマーカーが見やすくないと、hop.nvimの意味がない。派手な色を割り当てている。
学んだこと
1. 完璧を目指さない
最初から完璧なテーマを作ろうとすると、永遠に公開できない。v0.1.0で公開して、使いながら改善するのが正解だった。
2. 自分が使うものを作る
毎日使うからこそ、不満点が見えてくる。「誰かが使うかもしれない」ではなく「自分が使いたい」で作ると、モチベーションが続く。2年放置したけど。
3. 先人の知恵を借りる
他のカラーテーマのソースコードを読むと、「こうやって実装するのか」という発見がある。特にtokyonight.nvimは参考になった。
4. 2年放置しても動く
Neovimの後方互換性は素晴らしい。2年前のコードが、そのまま動いた。もちろんリファクタリングはしたが、「動かない」ということはなかった。
おわりに
カラーテーマ作成は、Neovimの内部構造を理解する良い機会だった。ハイライトグループの仕組み、Lua APIの使い方、プラグインとの連携方法など、普段は意識しない部分に触れることができた。
そして何より、自分だけのエディタ環境を作る楽しさがある。
「このテーマ、俺が作ったんだよ」と言えるのは、なかなか気分がいい。たとえ使っているのが自分だけでも。
興味があれば、ぜひ使ってみてほしい。バグ報告も歓迎する。次の改修は2028年になるかもしれないが。