feat: add contrast foreground config option

This commit is contained in:
Webhooked 2025-07-25 11:00:04 +02:00
commit 024999f6f9
7 changed files with 226 additions and 76 deletions

View file

@ -57,6 +57,30 @@ local palette = {
orange2 = "#b98d7b",
aqua = "#8ea4a2",
-- Contrast variants (20% more saturation)
redContrast = "#C93134",
red2Contrast = "#ED5965",
red3Contrast = "#CA675F",
yellowContrast = "#E59F49",
yellow2Contrast = "#EDC272",
yellow3Contrast = "#CAAC7A",
greenContrast = "#8FC055",
green2Contrast = "#7CAF7C",
green3Contrast = "#7F9F6E",
green4Contrast = "#5B9A82",
green5Contrast = "#6BAE97",
blueContrast = "#6EBBD4",
blue2Contrast = "#568B8F",
blue3Contrast = "#7EAABA",
blue4Contrast = "#81AAA9",
violetContrast = "#8A88B0",
violet2Contrast = "#7E91AF",
violet3Contrast = "#8A9FBE",
pinkContrast = "#A08AA2",
orangeContrast = "#BC8A6C",
orange2Contrast = "#BF856B",
aquaContrast = "#81AAA9",
-- Fg and Comments
fg = "#C5C9C7",
fg2 = "#f2f1ef",
@ -108,6 +132,30 @@ local palette = {
pearlTeal2 = "#6693bf",
pearlTeal3 = "#5a7785",
pearlCyan = "#d7e3d8",
-- Pearl contrast variants (40% more saturation)
pearlGreenContrast = "#5E8F2F",
pearlGreen2Contrast = "#5B9945",
pearlGreen3Contrast = "#A8DA9B",
pearlPinkContrast = "#C04062",
pearlOrangeContrast = "#E05700",
pearlOrange2Contrast = "#FF7700",
pearlYellowContrast = "#656720",
pearlYellow2Contrast = "#72612B",
pearlYellow3Contrast = "#F28C00",
pearlYellow4Contrast = "#FFD56D",
pearlRedContrast = "#D72436",
pearlRed2Contrast = "#E42D2C",
pearlRed3Contrast = "#F50000",
pearlRed4Contrast = "#E4977B",
pearlAquaContrast = "#3E8366",
pearlAqua2Contrast = "#428F6A",
pearlTeal1Contrast = "#2E96B0",
pearlTeal2Contrast = "#469FD3",
pearlTeal3Contrast = "#3D8077",
pearlBlue4Contrast = "#2A73B1",
pearlBlue5Contrast = "#3E56B8",
pearlViolet4Contrast = "#44418F",
}
local M = {}
@ -117,7 +165,7 @@ local M = {}
--- Defaults to KansoConfig.colors.
--- - theme: Use selected theme. Defaults to KansoConfig.theme
--- according to the value of 'background' option.
---@param opts? { colors?: table, theme?: string }
---@param opts? { colors?: table, theme?: string, foreground?: "default"|"contrast" }
---@return { theme: ThemeColors, palette: PaletteColors}
function M.setup(opts)
opts = opts or {}
@ -134,7 +182,14 @@ function M.setup(opts)
local updated_palette_colors = vim.tbl_extend("force", palette, override_colors.palette or {})
-- Generate the theme according to the updated palette colors
local theme_colors = require("kanso.themes")[theme](updated_palette_colors)
local kanso_config = require("kanso").config
local bg_mode = vim.o.background
local foreground = opts.foreground
or (type(kanso_config.foreground) == "table" and kanso_config.foreground[bg_mode])
or kanso_config.foreground
or "default"
---@cast foreground "default"|"contrast"
local theme_colors = require("kanso.themes")[theme](updated_palette_colors, foreground)
-- Add to and/or override theme_colors
local theme_overrides =

View file

@ -527,7 +527,7 @@ function M.setup(colors, config)
BufferLineBackground = { fg = theme.ui.none, bg = theme.ui.none },
BufferLineBuffer = { fg = theme.ui.none, bg = theme.ui.none },
BufferLineBufferSelected = { bg = theme.ui.none },
BufferLineBufferSelected = { bg = theme.ui.none, fg = theme.ui.fg },
BufferLineBufferVisible = { bg = theme.ui.none },
BufferLineCloseButton = { bg = theme.ui.none },
BufferLineCloseButtonSelected = { bg = theme.ui.none },

View file

@ -25,12 +25,14 @@ M.config = {
return {}
end,
---@type { dark: string, light: string }
background = { dark = "ink", light = "pearl" },
background = { dark = "ink", light = "ink" },
theme = "ink",
---@type { dark: "default"|"contrast", light: "default"|"contrast" }|"default"|"contrast"
foreground = "default",
compile = false,
}
local function check_config(config)
local function check_config(_)
local err
return not err
end
@ -60,6 +62,22 @@ function M.load(theme)
vim.g.colors_name = "kanso"
vim.o.termguicolors = true
-- Setup autocommand to reload theme when background changes
if not M._autocmd_created then
M._autocmd_created = true
vim.api.nvim_create_autocmd("OptionSet", {
pattern = "background",
callback = function()
if vim.g.colors_name == "kanso" then
-- Clear cached modules to force reload
package.loaded["kanso.colors"] = nil
package.loaded["kanso.themes"] = nil
M.load()
end
end,
})
end
if M.config.compile then
if utils.load_compiled(theme) then
return
@ -68,7 +86,11 @@ function M.load(theme)
M.compile()
utils.load_compiled(theme)
else
local colors = require("kanso.colors").setup({ theme = theme, colors = M.config.colors })
local foreground_setting = type(M.config.foreground) == "table" and M.config.foreground[vim.o.background]
or M.config.foreground
---@cast foreground_setting "default"|"contrast"
local colors =
require("kanso.colors").setup({ theme = theme, colors = M.config.colors, foreground = foreground_setting })
local highlights = require("kanso.highlights").setup(colors, M.config)
require("kanso.highlights").highlight(highlights, M.config.terminalColors and colors.theme.term or {})
end
@ -76,9 +98,45 @@ end
function M.compile()
for theme, _ in pairs(require("kanso.themes")) do
local colors = require("kanso.colors").setup({ theme = theme, colors = M.config.colors })
local highlights = require("kanso.highlights").setup(colors, M.config)
require("kanso.utils").compile(theme, highlights, M.config.terminalColors and colors.theme.term or {})
-- Compile both foreground variants if foreground is a table
if type(M.config.foreground) == "table" then
-- Compile for dark mode
local colors_dark = require("kanso.colors").setup({
theme = theme,
colors = M.config.colors,
foreground = M.config.foreground.dark,
})
local highlights_dark = require("kanso.highlights").setup(colors_dark, M.config)
require("kanso.utils").compile(
theme .. "_dark_" .. M.config.foreground.dark,
highlights_dark,
M.config.terminalColors and colors_dark.theme.term or {}
)
-- Compile for light mode
local colors_light = require("kanso.colors").setup({
theme = theme,
colors = M.config.colors,
foreground = M.config.foreground.light,
})
local highlights_light = require("kanso.highlights").setup(colors_light, M.config)
require("kanso.utils").compile(
theme .. "_light_" .. M.config.foreground.light,
highlights_light,
M.config.terminalColors and colors_light.theme.term or {}
)
else
-- Fallback for backward compatibility
local foreground_str = M.config.foreground
---@cast foreground_str "default"|"contrast"
local colors = require("kanso.colors").setup({
theme = theme,
colors = M.config.colors,
foreground = foreground_str,
})
local highlights = require("kanso.highlights").setup(colors, M.config)
require("kanso.utils").compile(theme, highlights, M.config.terminalColors and colors.theme.term or {})
end
end
end

View file

@ -1,5 +1,3 @@
local c = require("kanso.lib.color")
--TODO:
--PreProc needs its own color
--parameter and field should be different
@ -94,8 +92,9 @@ local c = require("kanso.lib.color")
return {
---@param palette PaletteColors
---@param foreground? "default"|"contrast"
---@return ThemeColors
zen = function(palette)
zen = function(palette, foreground)
return {
ui = {
none = "NONE",
@ -143,25 +142,25 @@ return {
},
},
syn = {
string = palette.green3,
string = foreground == "contrast" and palette.green3Contrast or palette.green3,
variable = "NONE",
number = palette.pink,
constant = palette.orange,
identifier = palette.violet2,
number = foreground == "contrast" and palette.pinkContrast or palette.pink,
constant = foreground == "contrast" and palette.orangeContrast or palette.orange,
identifier = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
parameter = palette.gray3,
fun = palette.blue3,
statement = palette.violet2,
keyword = palette.violet2,
fun = foreground == "contrast" and palette.blue3Contrast or palette.blue3,
statement = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
keyword = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
operator = palette.gray3,
preproc = palette.gray3,
type = palette.aqua,
regex = palette.red3,
type = foreground == "contrast" and palette.aquaContrast or palette.aqua,
regex = foreground == "contrast" and palette.red3Contrast or palette.red3,
deprecated = palette.gray,
punct = palette.gray3,
comment = palette.gray4,
special1 = palette.yellow3,
special2 = palette.violet2,
special3 = palette.violet2,
special1 = foreground == "contrast" and palette.yellow3Contrast or palette.yellow3,
special2 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
special3 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
},
diag = {
error = palette.red,
@ -205,8 +204,9 @@ return {
}
end,
---@param palette PaletteColors
---@param foreground? "default"|"contrast"
---@return ThemeColors
ink = function(palette)
ink = function(palette, foreground)
return {
ui = {
none = "NONE",
@ -254,25 +254,25 @@ return {
},
},
syn = {
string = palette.green3,
string = foreground == "contrast" and palette.green3Contrast or palette.green3,
variable = "NONE",
number = palette.pink,
constant = palette.orange,
identifier = palette.violet2,
number = foreground == "contrast" and palette.pinkContrast or palette.pink,
constant = foreground == "contrast" and palette.orangeContrast or palette.orange,
identifier = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
parameter = palette.gray3,
fun = palette.blue3,
statement = palette.violet2,
keyword = palette.violet2,
fun = foreground == "contrast" and palette.blue3Contrast or palette.blue3,
statement = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
keyword = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
operator = palette.gray3,
preproc = palette.gray3,
type = palette.aqua,
regex = palette.red3,
type = foreground == "contrast" and palette.aquaContrast or palette.aqua,
regex = foreground == "contrast" and palette.red3Contrast or palette.red3,
deprecated = palette.gray,
punct = palette.gray3,
comment = palette.gray4,
special1 = palette.yellow3,
special2 = palette.violet2,
special3 = palette.violet2,
special1 = foreground == "contrast" and palette.yellow3Contrast or palette.yellow3,
special2 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
special3 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
},
diag = {
error = palette.red,
@ -316,8 +316,9 @@ return {
}
end,
---@param palette PaletteColors
---@param foreground? "default"|"contrast"
---@return ThemeColors
pearl = function(palette)
pearl = function(palette, foreground)
return {
ui = {
none = "NONE",
@ -364,25 +365,25 @@ return {
},
},
syn = {
string = palette.pearlGreen,
string = foreground == "contrast" and palette.pearlGreenContrast or palette.pearlGreen,
variable = "NONE",
number = palette.pearlPink,
constant = palette.pearlOrange,
identifier = palette.pearlViolet4,
parameter = palette.pearlBlue5,
fun = palette.pearlBlue4,
statement = palette.pearlViolet4,
keyword = palette.pearlViolet4,
number = foreground == "contrast" and palette.pearlPinkContrast or palette.pearlPink,
constant = foreground == "contrast" and palette.pearlOrangeContrast or palette.pearlOrange,
identifier = foreground == "contrast" and palette.pearlViolet4Contrast or palette.pearlViolet4,
parameter = foreground == "contrast" and palette.pearlBlue5Contrast or palette.pearlBlue5,
fun = foreground == "contrast" and palette.pearlBlue4Contrast or palette.pearlBlue4,
statement = foreground == "contrast" and palette.pearlViolet4Contrast or palette.pearlViolet4,
keyword = foreground == "contrast" and palette.pearlViolet4Contrast or palette.pearlViolet4,
operator = palette.pearlGray3,
preproc = palette.pearlGray2,
type = palette.pearlAqua,
regex = palette.pearlYellow2,
type = foreground == "contrast" and palette.pearlAquaContrast or palette.pearlAqua,
regex = foreground == "contrast" and palette.pearlYellow2Contrast or palette.pearlYellow2,
deprecated = palette.pearlGray3,
comment = palette.pearlGray3,
punct = palette.pearlGray3,
special1 = palette.pearlYellow2,
special2 = palette.pearlViolet4,
special3 = palette.pearlViolet4,
special1 = foreground == "contrast" and palette.pearlYellow2Contrast or palette.pearlYellow2,
special2 = foreground == "contrast" and palette.pearlViolet4Contrast or palette.pearlViolet4,
special3 = foreground == "contrast" and palette.pearlViolet4Contrast or palette.pearlViolet4,
},
vcs = {
added = palette.pearlGreen2,
@ -426,8 +427,9 @@ return {
}
end,
---@param palette PaletteColors
---@param foreground? "default"|"contrast"
---@return ThemeColors
mist = function(palette)
mist = function(palette, foreground)
return {
ui = {
none = "NONE",
@ -475,25 +477,25 @@ return {
},
},
syn = {
string = palette.green3,
string = foreground == "contrast" and palette.green3Contrast or palette.green3,
variable = "NONE",
number = palette.pink,
constant = palette.orange,
identifier = palette.violet2,
number = foreground == "contrast" and palette.pinkContrast or palette.pink,
constant = foreground == "contrast" and palette.orangeContrast or palette.orange,
identifier = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
parameter = palette.gray3,
fun = palette.blue3,
statement = palette.violet2,
keyword = palette.violet2,
fun = foreground == "contrast" and palette.blue3Contrast or palette.blue3,
statement = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
keyword = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
operator = palette.gray3,
preproc = palette.gray3,
type = palette.aqua,
regex = palette.red3,
type = foreground == "contrast" and palette.aquaContrast or palette.aqua,
regex = foreground == "contrast" and palette.red3Contrast or palette.red3,
deprecated = palette.gray,
punct = palette.gray3,
comment = palette.gray4,
special1 = palette.yellow3,
special2 = palette.violet2,
special3 = palette.violet2,
special1 = foreground == "contrast" and palette.yellow3Contrast or palette.yellow3,
special2 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
special3 = foreground == "contrast" and palette.violet2Contrast or palette.violet2,
},
diag = {
error = palette.red,
@ -509,9 +511,9 @@ return {
text = palette.diffYellow,
},
vcs = {
added = palette.autumnGreen,
removed = palette.autumnRed,
changed = palette.autumnYellow,
added = palette.gitGreen,
removed = palette.gitRed,
changed = palette.gitYellow,
untracked = palette.gray4,
},
term = {

View file

@ -1,5 +1,5 @@
local M = {}
local PATH_SEP = vim.loop.os_uname().version:match("Windows") and "\\" or "/"
local PATH_SEP = vim.uv.os_uname().version:match("Windows") and "\\" or "/"
local get_compiled_path = function(theme)
return table.concat({ vim.fn.stdpath("state"), "kanso", theme .. "_compiled.lua" }, PATH_SEP)
@ -8,14 +8,14 @@ end
---@return string theme
function M.get_theme_from_bg_opt()
local config = require("kanso").config
return config.theme[vim.o.background] or config.theme.default
return config.background[vim.o.background] or config.theme
end
---@param theme string
---@param highlights table
---@param termcolors table
function M.compile(theme, highlights, termcolors)
vim.loop.fs_mkdir(vim.fn.stdpath("state") .. PATH_SEP .. "kanso", 448)
vim.uv.fs_mkdir(vim.fn.stdpath("state") .. PATH_SEP .. "kanso", 448)
local fname = get_compiled_path(theme)
local file, err = io.open(fname, "wb")

View file

@ -3,8 +3,8 @@ local theme = require("kanso.colors").setup().theme
local kanso = {}
kanso.normal = {
a = { bg = theme.syn.fun, fg = theme.ui.bg },
b = { bg = theme.ui.none, fg = theme.syn.fun },
a = { bg = theme.ui.fg, fg = theme.ui.bg },
b = { bg = theme.ui.none, fg = theme.ui.fg },
c = { bg = theme.ui.none, fg = theme.ui.fg },
}