Skip to content

Commit 0bcb3c6

Browse files
author
Venkata Subramani Renduchintala
committed
feat(themes): modular plug-n-play colorscheme system
Move each colorscheme into its own lua/themes/<name>.lua spec and rewrite lua/theme.lua as an auto-discovering loader. Adding a theme is now drop-in (no central table edit); switching is `:Theme <name>`, persisted to a machine-local state file and applied on the next launch. Only the active theme plugin is installed. Add five low-eyestrain dark themes: gruvbox_material_soft, everforest, kanagawa, tokyonight_moon and zenbones (requires lush.nvim). Switch lualine to theme="auto" so the statusline follows any active colorscheme; the previous theme_name string errors on themes without a matching lualine theme (e.g. zenbones, tokyonight_moon).
1 parent 77d9699 commit 0bcb3c6

20 files changed

Lines changed: 336 additions & 167 deletions

lua/plugins/configs/lualine.lua

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ return {
55
require("lualine").setup({
66
options = {
77
icons_enabled = true,
8-
theme = require("theme").theme_name,
8+
-- "auto" derives the statusline palette from whatever colorscheme
9+
-- is active, so any theme in lua/themes/ works without a matching
10+
-- lualine theme name.
11+
theme = "auto",
912
disabled_filetypes = {},
1013
always_divide_middle = true,
1114
globalstatus = true,

lua/theme.lua

Lines changed: 122 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1+
-- Plug-n-play theme system.
2+
--
3+
-- Each colorscheme lives in its own file under lua/themes/<name>.lua and returns
4+
-- a lazy.nvim plugin spec. To add a theme: drop a new file in lua/themes/ — no
5+
-- edits here required. To switch: `:Theme <name>` (persists across restarts) or
6+
-- set the default in lua/settings.lua via set_active_theme(...).
7+
--
8+
-- Only the *active* theme plugin is installed (get_active_theme marks it
9+
-- lazy=false / priority=1000); the others are never fetched, keeping startup lean.
10+
111
local M = {}
212

13+
-- Fixed accent palette consumed by some plugin configs (bufferline, scrollbar).
14+
-- Independent of the active colorscheme.
315
M.colors = {
416
bg = "#2e3440",
517
fg = "#ECEFF4",
@@ -33,177 +45,121 @@ M.colors = {
3345
grey19 = "#020203",
3446
}
3547

36-
local function loadNoClownFiesta()
37-
vim.cmd([[colorscheme no-clown-fiesta]])
38-
require("no-clown-fiesta").setup({
39-
transparent = false, -- Enable this to disable the bg color
40-
styles = {
41-
-- You can set any of the style values specified for `:h nvim_set_hl`
42-
comments = {},
43-
keywords = {},
44-
functions = {},
45-
variables = {},
46-
type = { bold = true },
47-
},
48-
})
48+
-- Machine-local persisted choice (not tracked in the repo).
49+
local state_file = vim.fn.stdpath("data") .. "/active-theme.txt"
50+
51+
-- Discover available theme names from lua/themes/*.lua on the runtimepath.
52+
function M.list()
53+
local seen, names = {}, {}
54+
for _, path in ipairs(vim.api.nvim_get_runtime_file("lua/themes/*.lua", true)) do
55+
local name = vim.fn.fnamemodify(path, ":t:r")
56+
if not seen[name] then
57+
seen[name] = true
58+
names[#names + 1] = name
59+
end
60+
end
61+
table.sort(names)
62+
return names
4963
end
5064

51-
local themes = {
52-
onenord = {
53-
"rmehri01/onenord.nvim",
54-
config = function()
55-
require('onenord').setup {
56-
borders = true,
57-
fade_nc = false,
58-
styles = {
59-
comments = "italic",
60-
strings = "NONE",
61-
keywords = "NONE",
62-
functions = "italic",
63-
variables = "bold",
64-
diagnostics = "underline"
65-
},
66-
disable = {
67-
background = false,
68-
cursorline = false,
69-
eob_lines = true
70-
},
71-
colors = {},
72-
}
73-
end
74-
},
75-
tokyonight = {
76-
"folke/tokyonight.nvim",
77-
config = function()
78-
local theme = require('tokyonight')
79-
theme.setup({
80-
style = 'night',
81-
on_colors = function(colors)
82-
colors.bg_dark = '#000000'
83-
colors.bg = '#11121D'
84-
-- colors.bg_visual = M.colors.grey12
85-
end
86-
})
87-
theme.load()
88-
end
89-
},
90-
onedark = {
91-
"navarasu/onedark.nvim",
92-
config = function()
93-
local theme = require('onedark')
94-
theme.setup {
95-
style = 'deep',
96-
transparent = false, -- Show/hide background
97-
code_style = {
98-
comments = 'italic',
99-
keywords = 'none',
100-
functions = 'none',
101-
strings = 'none',
102-
variables = 'none'
103-
},
104-
lualine = {
105-
transparent = true, -- lualine center bar transparency
106-
},
107-
}
108-
theme.load()
109-
-- loadNoClownFiesta()
110-
end
111-
},
112-
palenightfall = {
113-
"JoosepAlviste/palenightfall.nvim",
114-
config = function()
115-
require('palenightfall').setup {}
116-
end
117-
},
118-
nordic = {
119-
"AlexvZyl/nordic.nvim",
120-
config = function()
121-
require('nordic').setup {}
122-
end
123-
},
124-
onedarkpro = {
125-
"olimorris/onedarkpro.nvim",
126-
config = function()
127-
vim.o.background = "dark"
128-
require('onedarkpro').load()
129-
end
130-
},
131-
tokyodark = {
132-
"tiagovla/tokyodark.nvim",
133-
config = function()
134-
vim.g.tokyodark_transparent_background = false
135-
vim.g.tokyodark_enable_italic_comment = true
136-
vim.g.tokyodark_enable_italic = true
137-
vim.g.tokyodark_color_gamma = "0.0"
138-
vim.cmd 'colorscheme tokyodark'
139-
end
140-
},
141-
moonfly = {
142-
"bluz71/vim-moonfly-colors",
143-
config = function()
144-
vim.cmd [[colorscheme moonfly]]
145-
end
146-
},
147-
dracula = {
148-
"Mofiqul/dracula.nvim",
149-
config = function()
150-
local theme = require('dracula')
151-
theme.setup {}
152-
theme.load()
153-
end
154-
},
155-
draculanight = {
156-
"magidc/draculanight",
157-
config = function()
158-
local theme = require('draculanight')
159-
theme.setup {}
160-
theme.load()
161-
end
162-
},
163-
catppuccin = {
164-
"catppuccin/nvim",
165-
name = "catppuccin",
166-
config = function()
167-
vim.g.catppuccin_flavour = "mocha" -- latte, frappe, macchiato, mocha
168-
vim.cmd [[colorscheme catppuccin]]
169-
end
170-
},
171-
material = {
172-
"marko-cerovac/material.nvim",
173-
config = function()
174-
require "plugins.configs.materialui"
175-
end
176-
},
177-
gruvbox = {
178-
'sainnhe/gruvbox-material',
179-
lazy = false,
180-
config = function()
181-
-- Optionally configure and load the colorscheme
182-
-- directly inside the plugin declaration.
183-
vim.g.gruvbox_material_enable_italic = true
184-
vim.cmd.colorscheme('gruvbox-material')
185-
vim.g.show_eob = 1
186-
vim.g.background = 'dark'
187-
vim.g.gruvbox_material_background = 'hard'
188-
vim.g.gruvbox_material_foreground = 'material'
189-
vim.g.gruvbox_material_cursor = 'auto'
190-
vim.g.gruvbox_material_show_eob = 0
191-
vim.g.gruvbox_material_ui_contrast = 'high'
192-
vim.g.gruvbox_material_float_style = 'bright'
193-
vim.g.gruvbox_material_transparent_background = 2
194-
end
195-
},
196-
}
65+
local function is_valid(name)
66+
for _, n in ipairs(M.list()) do
67+
if n == name then
68+
return true
69+
end
70+
end
71+
return false
72+
end
73+
74+
local function read_persisted()
75+
local f = io.open(state_file, "r")
76+
if not f then
77+
return nil
78+
end
79+
local name = vim.trim(f:read("*a") or "")
80+
f:close()
81+
return name ~= "" and name or nil
82+
end
19783

198-
M.set_active_theme = function(theme_name)
199-
M.theme_name = theme_name
84+
local function write_persisted(name)
85+
local f = io.open(state_file, "w")
86+
if not f then
87+
return false
88+
end
89+
f:write(name)
90+
f:close()
91+
return true
20092
end
20193

202-
M.get_active_theme = function()
203-
local theme = themes[M.theme_name]
204-
theme.lazy = false
205-
theme.priority = 1000
206-
return theme
94+
-- Default theme, set from lua/settings.lua. A persisted `:Theme` choice wins.
95+
function M.set_active_theme(name)
96+
M.default_theme = name
97+
if not M.theme_name then
98+
M.theme_name = name
99+
end
207100
end
208101

102+
local function resolve_name()
103+
local persisted = read_persisted()
104+
if persisted and is_valid(persisted) then
105+
return persisted
106+
end
107+
if M.default_theme and is_valid(M.default_theme) then
108+
return M.default_theme
109+
end
110+
return M.default_theme or M.list()[1]
111+
end
112+
113+
-- Returns the lazy.nvim spec for the active theme (the only one that gets installed).
114+
function M.get_active_theme()
115+
local name = resolve_name()
116+
M.theme_name = name
117+
local ok, spec = pcall(require, "themes." .. name)
118+
if not ok or type(spec) ~= "table" then
119+
vim.notify("theme: failed to load 'themes." .. tostring(name) .. "': " .. tostring(spec), vim.log.levels.ERROR)
120+
return {}
121+
end
122+
spec.lazy = false
123+
spec.priority = 1000
124+
return spec
125+
end
126+
127+
-- :Theme -> show current + available
128+
-- :Theme <name> -> persist choice (restart to apply; only active is installed)
129+
vim.api.nvim_create_user_command("Theme", function(opts)
130+
local name = vim.trim(opts.args)
131+
local themes = M.list()
132+
if name == "" then
133+
vim.notify(
134+
"Active theme: " .. (M.theme_name or "?") .. "\nAvailable: " .. table.concat(themes, ", "),
135+
vim.log.levels.INFO
136+
)
137+
return
138+
end
139+
if not is_valid(name) then
140+
vim.notify("Unknown theme '" .. name .. "'. Available: " .. table.concat(themes, ", "), vim.log.levels.ERROR)
141+
return
142+
end
143+
if write_persisted(name) then
144+
vim.notify(
145+
"Theme set to '" .. name .. "'. Restart Neovim to apply (it installs on next launch).",
146+
vim.log.levels.INFO
147+
)
148+
else
149+
vim.notify("Could not write theme choice to " .. state_file, vim.log.levels.ERROR)
150+
end
151+
end, {
152+
nargs = "?",
153+
desc = "Show or switch the active colorscheme (restart-based)",
154+
complete = function(arglead)
155+
local out = {}
156+
for _, n in ipairs(M.list()) do
157+
if n:find(arglead, 1, true) == 1 then
158+
out[#out + 1] = n
159+
end
160+
end
161+
return out
162+
end,
163+
})
164+
209165
return M

lua/themes/catppuccin.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
return {
2+
"catppuccin/nvim",
3+
name = "catppuccin",
4+
config = function()
5+
vim.g.catppuccin_flavour = "mocha" -- latte, frappe, macchiato, mocha
6+
vim.cmd([[colorscheme catppuccin]])
7+
end,
8+
}

lua/themes/dracula.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
return {
2+
"Mofiqul/dracula.nvim",
3+
config = function()
4+
local theme = require("dracula")
5+
theme.setup({})
6+
theme.load()
7+
end,
8+
}

lua/themes/draculanight.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
return {
2+
"magidc/draculanight",
3+
config = function()
4+
local theme = require("draculanight")
5+
theme.setup({})
6+
theme.load()
7+
end,
8+
}

lua/themes/everforest.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- Everforest — warm green-grey, muted, designed for low fatigue.
2+
return {
3+
"sainnhe/everforest",
4+
config = function()
5+
vim.o.background = "dark"
6+
vim.g.everforest_background = "medium" -- hard | medium | soft
7+
vim.g.everforest_foreground = "material"
8+
vim.g.everforest_ui_contrast = "low"
9+
vim.g.everforest_enable_italic = true
10+
vim.g.everforest_better_performance = 1
11+
vim.g.everforest_transparent_background = 0
12+
vim.cmd.colorscheme("everforest")
13+
end,
14+
}

lua/themes/gruvbox.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- Gruvbox Material — high-contrast / hard background (original tuning).
2+
return {
3+
"sainnhe/gruvbox-material",
4+
config = function()
5+
vim.g.gruvbox_material_enable_italic = true
6+
vim.cmd.colorscheme("gruvbox-material")
7+
vim.g.show_eob = 1
8+
vim.g.background = "dark"
9+
vim.g.gruvbox_material_background = "hard"
10+
vim.g.gruvbox_material_foreground = "material"
11+
vim.g.gruvbox_material_cursor = "auto"
12+
vim.g.gruvbox_material_show_eob = 0
13+
vim.g.gruvbox_material_ui_contrast = "high"
14+
vim.g.gruvbox_material_float_style = "bright"
15+
vim.g.gruvbox_material_transparent_background = 2
16+
end,
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Gruvbox Material, readability-tuned: medium background + low UI contrast +
2+
-- no transparency to avoid halation. Low-eyestrain counterpart to "gruvbox".
3+
return {
4+
"sainnhe/gruvbox-material",
5+
config = function()
6+
vim.o.background = "dark"
7+
vim.g.gruvbox_material_background = "medium" -- soft | medium | hard
8+
vim.g.gruvbox_material_foreground = "material"
9+
vim.g.gruvbox_material_ui_contrast = "low" -- low | high
10+
vim.g.gruvbox_material_enable_italic = true
11+
vim.g.gruvbox_material_better_performance = 1
12+
vim.g.gruvbox_material_transparent_background = 0
13+
vim.cmd.colorscheme("gruvbox-material")
14+
end,
15+
}

0 commit comments

Comments
 (0)