vim.cmd('syntax on') local function detect_background() vim.system( { 'defaults', 'read', '-g', 'AppleInterfaceStyle' }, { text = true }, function(out) local bg = (out.stdout or ''):match('Dark') and 'dark' or 'light' vim.schedule(function() if bg ~= vim.o.background then vim.o.background = bg end end) end ) end -- All treesitter capture groups to neutralize local TS_GROUPS = { '@variable', '@variable.builtin', '@variable.parameter', '@variable.member', '@module', '@module.builtin', '@label', '@string', '@string.documentation', '@string.escape', '@string.special', '@string.special.symbol', '@string.special.url', '@character', '@character.special', '@boolean', '@number', '@number.float', '@float', '@function', '@function.builtin', '@function.call', '@function.macro', '@function.method', '@function.method.call', '@constructor', '@operator', '@keyword', '@keyword.coroutine', '@keyword.function', '@keyword.operator', '@keyword.import', '@keyword.type', '@keyword.modifier', '@keyword.repeat', '@keyword.return', '@keyword.debug', '@keyword.exception', '@keyword.conditional', '@keyword.conditional.ternary', '@keyword.directive', '@keyword.directive.define', '@punctuation', '@punctuation.delimiter', '@punctuation.bracket', '@punctuation.special', '@type', '@type.builtin', '@type.definition', '@attribute', '@attribute.builtin', '@property', '@constant', '@constant.builtin', '@constant.macro', '@namespace', '@field', '@parameter', '@conditional', '@repeat', '@include', '@exception', '@tag', '@tag.builtin', '@tag.attribute', '@tag.delimiter', '@text', '@text.title', '@text.literal', '@text.reference', '@text.emphasis', '@text.strong', '@text.uri', '@text.strikethrough', '@text.todo', '@text.note', '@text.warning', '@text.danger', '@markup', '@markup.heading', '@markup.heading.1', '@markup.heading.2', '@markup.heading.3', '@markup.heading.4', '@markup.heading.5', '@markup.heading.6', '@markup.quote', '@markup.link', '@markup.link.label', '@markup.link.url', '@markup.raw', '@markup.raw.block', '@markup.list', '@markup.list.checked', '@markup.list.unchecked', '@markup.italic', '@markup.bold', '@markup.strikethrough', '@markup.underline', '@markup.math', '@diff.plus', '@diff.minus', '@diff.delta', -- LSP semantic tokens '@lsp.type.class', '@lsp.type.comment', '@lsp.type.decorator', '@lsp.type.enum', '@lsp.type.enumMember', '@lsp.type.event', '@lsp.type.function', '@lsp.type.interface', '@lsp.type.keyword', '@lsp.type.macro', '@lsp.type.method', '@lsp.type.modifier', '@lsp.type.namespace', '@lsp.type.number', '@lsp.type.operator', '@lsp.type.parameter', '@lsp.type.property', '@lsp.type.regexp', '@lsp.type.string', '@lsp.type.struct', '@lsp.type.type', '@lsp.type.typeParameter', '@lsp.type.variable', '@lsp.mod.abstract', '@lsp.mod.async', '@lsp.mod.declaration', '@lsp.mod.defaultLibrary', '@lsp.mod.definition', '@lsp.mod.deprecated', '@lsp.mod.documentation', '@lsp.mod.modification', '@lsp.mod.readonly', '@lsp.mod.static', } local SYNTAX_GROUPS = { 'Statement', 'Keyword', 'Function', 'Type', 'PreProc', 'Special', 'Identifier', 'Constant', 'String', 'Number', 'Boolean', 'Operator', 'Title', 'Bold', 'Italic', 'Include', 'Define', 'Macro', 'Structure', 'StorageClass', 'Typedef', 'Tag', 'Delimiter', 'SpecialComment', 'Todo', } local function apply() vim.cmd('hi clear') local is_dark = vim.o.background == 'dark' -- icy palette local comment_fg = is_dark and '#6b635a' or '#776e63' -- matches Zed comment color local subtle = is_dark and '#4a443d' or '#fceccc' -- borders, indent guides local sel_bg = is_dark and '#509c93' or '#fceccc' -- matches Ghostty selection local sel_fg = is_dark and '#fff6e3' or '#4a443d' local active_bg = is_dark and '#2a261f' or '#f5f0e6' -- active line / pmenu bg -- Normal: transparent so Ghostty bg shows through vim.api.nvim_set_hl(0, 'Normal', { bg = 'NONE', fg = 'NONE' }) vim.api.nvim_set_hl(0, 'NormalNC', { bg = 'NONE', fg = 'NONE' }) -- Comments: lighter + italic (only syntax highlight) vim.api.nvim_set_hl(0, 'Comment', { fg = comment_fg, italic = true }) vim.api.nvim_set_hl(0, '@comment', { fg = comment_fg, italic = true }) vim.api.nvim_set_hl(0, '@comment.documentation', { fg = comment_fg, italic = true }) -- Neutralize all treesitter and LSP semantic token groups for _, group in ipairs(TS_GROUPS) do vim.api.nvim_set_hl(0, group, {}) end for _, group in ipairs(SYNTAX_GROUPS) do vim.api.nvim_set_hl(0, group, {}) end -- Non-text / whitespace / indent guides vim.api.nvim_set_hl(0, 'Whitespace', { fg = subtle }) vim.api.nvim_set_hl(0, 'NonText', { fg = subtle }) vim.api.nvim_set_hl(0, 'EndOfBuffer', { fg = subtle }) vim.api.nvim_set_hl(0, 'SpecialKey', { fg = subtle }) -- Line numbers vim.api.nvim_set_hl(0, 'LineNr', { fg = subtle }) vim.api.nvim_set_hl(0, 'CursorLineNr', { fg = comment_fg }) vim.api.nvim_set_hl(0, 'SignColumn', { bg = 'NONE' }) -- Cursor line (if cursorline is ever enabled) vim.api.nvim_set_hl(0, 'CursorLine', { bg = active_bg }) vim.api.nvim_set_hl(0, 'CursorColumn', { bg = active_bg }) -- Selection / search: match Ghostty selection colors vim.api.nvim_set_hl(0, 'Visual', { bg = sel_bg, fg = sel_fg }) vim.api.nvim_set_hl(0, 'Search', { bg = sel_bg, fg = sel_fg }) vim.api.nvim_set_hl(0, 'IncSearch', { bg = sel_bg, fg = sel_fg }) vim.api.nvim_set_hl(0, 'CurSearch', { bg = sel_bg, fg = sel_fg }) vim.api.nvim_set_hl(0, 'Substitute', { bg = sel_bg, fg = sel_fg }) -- Matching parens: subtle underline, no color vim.api.nvim_set_hl(0, 'MatchParen', { underline = true }) -- Popup menu vim.api.nvim_set_hl(0, 'Pmenu', { bg = active_bg }) vim.api.nvim_set_hl(0, 'PmenuSel', { bg = sel_bg, fg = sel_fg }) vim.api.nvim_set_hl(0, 'PmenuSbar', { bg = subtle }) vim.api.nvim_set_hl(0, 'PmenuThumb', { bg = comment_fg }) -- Window / float borders vim.api.nvim_set_hl(0, 'WinSeparator', { fg = subtle }) vim.api.nvim_set_hl(0, 'VertSplit', { fg = subtle }) vim.api.nvim_set_hl(0, 'NormalFloat', { bg = 'NONE', fg = 'NONE' }) vim.api.nvim_set_hl(0, 'FloatBorder', { fg = comment_fg }) -- Status / tab line: transparent vim.api.nvim_set_hl(0, 'StatusLine', { bg = 'NONE', fg = 'NONE' }) vim.api.nvim_set_hl(0, 'StatusLineNC', { bg = 'NONE', fg = 'NONE' }) vim.api.nvim_set_hl(0, 'TabLine', { bg = 'NONE', fg = comment_fg }) vim.api.nvim_set_hl(0, 'TabLineSel', { bg = 'NONE', fg = 'NONE' }) vim.api.nvim_set_hl(0, 'TabLineFill', { bg = 'NONE' }) -- Folds vim.api.nvim_set_hl(0, 'Folded', { fg = comment_fg }) vim.api.nvim_set_hl(0, 'FoldColumn', { fg = subtle, bg = 'NONE' }) -- Diff: icy palette tinted bgs, no fg colors (monochrome) if is_dark then vim.api.nvim_set_hl(0, 'DiffAdd', { bg = '#1a2a10' }) vim.api.nvim_set_hl(0, 'DiffDelete', { bg = '#2d1616' }) vim.api.nvim_set_hl(0, 'DiffChange', { bg = '#0f2525' }) vim.api.nvim_set_hl(0, 'DiffText', { bg = '#1a3a38' }) vim.api.nvim_set_hl(0, 'diffAdded', { fg = '#7c9f4b' }) vim.api.nvim_set_hl(0, 'diffRemoved', { fg = '#db7070' }) vim.api.nvim_set_hl(0, 'diffChanged', { fg = '#509c93' }) else vim.api.nvim_set_hl(0, 'DiffAdd', { bg = '#e8f3d8' }) vim.api.nvim_set_hl(0, 'DiffDelete', { bg = '#f9e0e0' }) vim.api.nvim_set_hl(0, 'DiffChange', { bg = '#daf0ed' }) vim.api.nvim_set_hl(0, 'DiffText', { bg = '#b8e0dc' }) vim.api.nvim_set_hl(0, 'diffAdded', { fg = '#7c9f4b' }) vim.api.nvim_set_hl(0, 'diffRemoved', { fg = '#db7070' }) vim.api.nvim_set_hl(0, 'diffChanged', { fg = '#509c93' }) end -- Diagnostics: underline only, signs/virtual text in comment color for _, kind in ipairs({ 'Error', 'Warn', 'Info', 'Hint' }) do vim.api.nvim_set_hl(0, 'Diagnostic' .. kind, { fg = comment_fg }) vim.api.nvim_set_hl(0, 'DiagnosticUnderline' .. kind, { underline = true }) vim.api.nvim_set_hl(0, 'DiagnosticVirtualText' .. kind, { fg = comment_fg }) vim.api.nvim_set_hl(0, 'DiagnosticSign' .. kind, { fg = comment_fg }) vim.api.nvim_set_hl(0, 'DiagnosticFloating' .. kind, { fg = comment_fg }) end -- Spell: underline only for _, kind in ipairs({ 'Bad', 'Cap', 'Rare', 'Local' }) do vim.api.nvim_set_hl(0, 'Spell' .. kind, { underline = true, sp = comment_fg }) end -- Git signs (gitsigns): icy palette colors vim.api.nvim_set_hl(0, 'GitSignsAdd', { fg = '#7c9f4b' }) vim.api.nvim_set_hl(0, 'GitSignsChange', { fg = '#509c93' }) vim.api.nvim_set_hl(0, 'GitSignsDelete', { fg = '#db7070' }) end detect_background() apply() vim.api.nvim_create_autocmd('OptionSet', { pattern = 'background', callback = apply }) vim.api.nvim_create_autocmd('FocusGained', { callback = detect_background }) vim.api.nvim_create_autocmd('VimEnter', { once = true, callback = apply }) -- Re-clear LSP semantic tokens after LspAttach (set after colorscheme loads) vim.api.nvim_create_autocmd('LspAttach', { callback = function() for _, group in ipairs(vim.fn.getcompletion('@lsp', 'highlight')) do if not group:match('comment') then vim.api.nvim_set_hl(0, group, {}) end end end, })