initial commit

This commit is contained in:
Barrett Ruth 2025-11-08 13:04:35 -05:00
commit 4c257d5065
9 changed files with 493 additions and 0 deletions

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Raphael
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

64
README.md Normal file
View file

@ -0,0 +1,64 @@
# midnight.nvim
A minimal, scientifically-optimized colorscheme with automatic dark/light variants.
## Features
- **Single theme, dual modes**: Automatically detects `vim.o.background` and switches between midnight (dark) and daylight (light) palettes
- **Ultra-minimal syntax**: Only 3 semantic colors - blue for keywords, green for literals, grey for comments, white for everything else
- **Scientific optimization**: Desaturated colors in the 200-50 range, WCAG AAA contrast ratios
- **Eye strain reduction**: No pure black (#121212) or pure white (#e0e0e0)
- **Full terminal support**: Complete 16 ANSI color support
- **Plugin compatibility**: Built-in support for fzf-lua, nvim-cmp, gitsigns, and more
## Installation
### lazy.nvim
```lua
{
'barrett-ruth/midnight.nvim',
lazy = false,
priority = 1000,
config = function()
require('midnight').setup()
vim.cmd('colorscheme midnight')
end,
}
```
## Usage
```vim
colorscheme midnight
```
The theme automatically adapts to your background setting:
```lua
vim.o.background = 'dark' -- uses midnight palette
vim.o.background = 'light' -- uses daylight palette
```
## Design Philosophy
- **Minimal colors**: Only color what's semantically necessary
- **High contrast**: Meet WCAG AAA standards for readability
- **Consistent with terminal**: Matches Ghostty, tmux, and FZF themes
- **No AI comments**: Clean, human-written code
## Color Palette
### Midnight (Dark)
- Background: `#121212` (optimal dark gray)
- Foreground: `#e0e0e0` (87% white)
- Blue: `#7aa2f7` (keywords/control)
- Green: `#98c379` (literals/data)
- Grey: `#666666` (comments)
### Daylight (Light)
- Optimized light variant with similar principles
## License
MIT

1
colors/midnight.lua Normal file
View file

@ -0,0 +1 @@
require('midnight').load()

24
lua/midnight/init.lua Normal file
View file

@ -0,0 +1,24 @@
local M = {}
---@param opts? table
function M.setup(opts)
opts = opts or {}
end
function M.load()
if vim.g.colors_name then
vim.cmd('hi clear')
end
if vim.fn.exists('syntax_on') then
vim.cmd('syntax reset')
end
vim.o.termguicolors = true
vim.g.colors_name = 'midnight'
local theme = require('midnight.theme')
theme.apply()
end
return M

96
lua/midnight/palette.lua Normal file
View file

@ -0,0 +1,96 @@
local M = {}
---@class Palette
---@field black string
---@field red string
---@field green string
---@field yellow string
---@field blue string
---@field magenta string
---@field cyan string
---@field orange? string
---@field white string
---@field bright_black string
---@field bright_red string
---@field bright_green string
---@field bright_yellow string
---@field bright_blue string
---@field bright_magenta string
---@field bright_cyan string
---@field bright_white string
---@field light_black string
---@field grey string
---@field med_grey? string
---@field dark_grey? string
---@field light_grey? string
---@field light_red? string
---@field light_green? string
---@field light_yellow? string
---@field light_blue? string
---@field light_magenta? string
---@field light_cyan? string
---@field light_white? string
---@field medium_emphasis? string
---@type Palette
M.midnight = {
black = '#121212',
red = '#f48771',
green = '#98c379',
yellow = '#e5c07b',
blue = '#7aa2f7',
magenta = '#c678dd',
cyan = '#56b6c2',
orange = '#e5a56b',
white = '#e0e0e0',
bright_black = '#666666',
bright_red = '#ff6b6b',
bright_green = '#b5e890',
bright_yellow = '#f0d197',
bright_blue = '#9db8f7',
bright_magenta = '#e298ff',
bright_cyan = '#7dd6e0',
bright_white = '#ffffff',
light_black = '#666666',
grey = '#3d3d3d',
med_grey = '#2d2d2d',
dark_grey = '#1e1e1e',
medium_emphasis = '#999999',
}
---@type Palette
M.daylight = {
black = '#000000',
grey = '#666666',
red = '#B22222',
green = '#228B22',
yellow = '#B8860B',
blue = '#27408B',
magenta = '#8B008B',
cyan = '#00BFFF',
white = '#ffffff',
light_black = '#555555',
light_grey = '#ECECEC',
light_red = '#ff0000',
light_green = '#00ff00',
light_yellow = '#ffa500',
light_blue = '#0000ff',
light_magenta = '#ff00ff',
light_cyan = '#00ffff',
light_white = '#ffffff',
}
---@return Palette
function M.get_palette()
if vim.o.background == 'light' then
return M.daylight
else
return M.midnight
end
end
return M

248
lua/midnight/theme.lua Normal file
View file

@ -0,0 +1,248 @@
local M = {}
---@class Highlight
---@field fg? string
---@field bg? string
---@field bold? boolean
---@field italic? boolean
---@field underline? boolean
---@field undercurl? boolean
---@field reverse? boolean
---@field special? string
---@field none? boolean
---@param from string
---@param tos string|string[]
local function link(from, tos)
tos = type(tos) == 'string' and { tos } or tos
for _, to in ipairs(tos) do
vim.api.nvim_set_hl(0, to, { link = from })
end
end
---@param group string
---@param highlights Highlight
---@param links? string[]
local function hi(group, highlights, links)
if highlights.none then
highlights.none = nil
highlights.undercurl = false
highlights.italic = false
highlights.bold = false
highlights.fg = 'NONE'
highlights.bg = 'NONE'
end
vim.api.nvim_set_hl(0, group, highlights)
for _, other in ipairs(links or {}) do
link(group, other)
end
end
---@param group string
---@param highlights Highlight
---@param links? string[]
local function tshi(group, highlights, links)
hi(group, highlights)
local tsgroup = '@' .. group:gsub('^%L', string.lower)
link(group, tsgroup)
for _, to in ipairs(links or {}) do
link(group, to)
end
end
---@return nil
function M.apply()
local palette = require('midnight.palette')
local cs = palette.get_palette()
for color, hexcode in pairs(cs) do
hi(color, { fg = hexcode })
end
hi(
'Normal',
{ fg = cs.white, bg = cs.black },
{ 'Identifier', 'Special', 'StatusLine', 'StatusLineNC', 'Winbar', 'WinbarNC' }
)
hi('NonText', { fg = cs.grey }, { 'SpecialKey' })
hi('LineNr', { fg = cs.light_black }, { 'SignColumn' })
hi('CursorLine', { bg = cs.dark_grey }, { 'ColorColumn', 'Folded', 'Visual' })
hi('CursorLineNr', { fg = cs.medium_emphasis })
hi('Conceal', { fg = cs.light_black, bg = cs.bg })
hi('Directory', { fg = cs.white })
hi('Error', { fg = cs.red })
hi('ErrorMsg', { bold = true, underline = true, fg = cs.red })
hi('MoreMsg', { fg = cs.yellow }, { 'WarningMsg' })
hi('MatchParen', { bg = cs.med_grey })
hi('NormalFloat', { bg = cs.bg }, {
'LspInfoBorder',
'FloatBorder',
'FloatShadow',
'FloatShadowThrough',
})
hi('Search', { reverse = true }, { 'IncSearch' })
hi('Whitespace', { fg = cs.grey })
tshi('Boolean', { fg = cs.green }, { '@constant.builtin' })
tshi('Comment', { fg = cs.light_black })
tshi('Constant', { fg = cs.green })
tshi('Define', { fg = cs.blue })
tshi('Function', { fg = cs.white }, { '@function.builtin', '@function.macro' })
tshi('Include', { fg = cs.blue })
tshi('Keyword', { fg = cs.blue }, { 'Statement' })
tshi('Namespace', { fg = cs.white })
tshi('Number', { fg = cs.green })
tshi(
'Operator',
{ fg = cs.white },
{ '@keyword.operator', '@conditional.ternary' }
)
hi('Delimiter', { none = true })
hi('@punctuation.delimiter', { fg = cs.white })
tshi('PreProc', { fg = cs.blue })
tshi('String', { fg = cs.green }, { '@character' })
hi('@string.escape', { fg = cs.green })
tshi('Title', { bold = true, fg = cs.white })
hi('@tag', { fg = cs.white })
hi('@tag.attribute', { fg = cs.white })
hi('@tag.delimiter', { fg = cs.white })
tshi('Type', { fg = cs.white })
hi('@type.qualifier', { fg = cs.white }, { '@storageclass' })
hi('@lsp.type.enum', { fg = cs.white }, { '@lsp.type.class' })
hi('@lsp.type.comment', { none = true }, { '@lsp.type.macro' })
hi('@text.emphasis', { italic = true })
hi('@text.strong', { bold = true })
hi('@text.underline', { underline = true })
hi('@text.uri', { fg = cs.white, underline = true }, { '@text.reference' })
hi('@text.danger', { fg = cs.red, bold = true, italic = true })
hi('@text.note', { fg = cs.green, bold = true, italic = true })
hi('@text.todo', { fg = cs.yellow, bold = true, italic = true }, { 'Todo' })
hi('@text.warning', { fg = cs.yellow, bold = true, italic = true })
hi('@variable', { none = true })
hi('Pmenu', { bg = cs.med_grey }, { 'PmenuSbar' })
hi('PmenuSel', { fg = cs.med_grey, bg = cs.medium_emphasis })
hi('PmenuThumb', { bg = cs.medium_emphasis })
hi('LspInlayHint', { fg = cs.light_black })
hi('LspSignatureActiveParameter', { underline = true, italic = true })
hi(
'DiagnosticError',
{ fg = cs.red },
{ 'DiagnosticFloatingError', 'DiagnosticSignError' }
)
hi(
'DiagnosticWarn',
{ fg = cs.yellow },
{ 'DiagnosticFloatingWarn', 'DiagnosticSignWarn' }
)
hi(
'DiagnosticHint',
{ fg = cs.white },
{ 'DiagnosticFloatingHint', 'DiagnosticSignHint' }
)
hi(
'DiagnosticOk',
{ fg = cs.green },
{ 'DiagnosticFloatingOk', 'DiagnosticSignOk' }
)
hi(
'DiagnosticInfo',
{ fg = cs.white },
{ 'DiagnosticFloatingInfo', 'DiagnosticSignInfo' }
)
hi('DiagnosticUnderlineError', { undercurl = true, special = cs.red })
hi('DiagnosticUnderlineWarn', { undercurl = true, special = cs.yellow })
hi('DiagnosticUnderlineHint', { undercurl = true, special = cs.white })
hi('DiagnosticUnderlineOk', { undercurl = true, special = cs.green })
hi('DiagnosticUnderlineInfo', { undercurl = true, special = cs.white })
hi('SpellBad', { underline = true, special = cs.red })
hi('SpellRare', { underline = true, special = cs.white })
hi('SpellCap', { underline = true, special = cs.white })
hi('SpellLocal', { underline = true, special = cs.white })
hi('gitCommitSummary', { fg = cs.white })
hi('@attribute.diff', { fg = cs.white })
hi('DiffAdd', { fg = cs.green }, { '@text.diff.add', 'diffAdded' })
hi('DiffDelete', { fg = cs.red }, { '@text.diff.delete', 'diffRemoved' })
hi('DiffChange', { fg = cs.yellow })
hi('@constructor.lua', { fg = cs.white })
hi('@markup.heading.gitcommit', { none = true })
hi('GitSignsCurrentLineBlame', { italic = true, fg = cs.light_black })
link('DiffAdd', 'GitSignsAdd')
link('DiffChange', 'GitSignsChange')
link('DiffDelete', 'GitSignsDelete')
link('Search', 'HighlightUndo')
hi('CmpItemAbbrMatch', { fg = cs.white, bold = true })
for hlgroup, color in pairs({
Key = 'white',
Keyword = 'blue',
Folder = 'white',
File = 'white',
Boolean = 'green',
Class = 'white',
Constant = 'green',
Constructor = 'white',
Enum = 'white',
EnumMember = 'white',
Field = 'white',
Function = 'white',
Interface = 'white',
Method = 'white',
Namespace = 'white',
Null = 'white',
Number = 'green',
Operator = 'white',
Property = 'white',
String = 'green',
Struct = 'white',
Text = 'white',
TypeParameter = 'white',
Variable = 'white',
}) do
hi('CmpItemKind' .. hlgroup, { fg = cs[color] })
end
link('NormalFloat', 'NullLsInfoBorder')
link('Directory', 'OilDir')
link('NormalFloat', 'FzfLuaBorder')
hi('FzfLuaHeaderText', { fg = cs.white }, { 'FzfLuaBufFlagCur' })
hi('FzfLuaBufFlagAlt', { fg = cs.white })
vim.g.terminal_color_0 = cs.black
vim.g.terminal_color_1 = cs.red
vim.g.terminal_color_2 = cs.green
vim.g.terminal_color_3 = cs.yellow
vim.g.terminal_color_4 = cs.blue
vim.g.terminal_color_5 = cs.magenta
vim.g.terminal_color_6 = cs.cyan
vim.g.terminal_color_7 = cs.white
vim.g.terminal_color_8 = cs.bright_black
vim.g.terminal_color_9 = cs.bright_red
vim.g.terminal_color_10 = cs.bright_green
vim.g.terminal_color_11 = cs.bright_yellow
vim.g.terminal_color_12 = cs.bright_blue
vim.g.terminal_color_13 = cs.bright_magenta
vim.g.terminal_color_14 = cs.bright_cyan
vim.g.terminal_color_15 = cs.bright_white
end
return M

1
selene.toml Normal file
View file

@ -0,0 +1 @@
std = 'vim'

8
stylua.toml Normal file
View file

@ -0,0 +1,8 @@
column_width = 100
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
call_parentheses = "Always"
[sort_requires]
enabled = true

30
vim.toml Normal file
View file

@ -0,0 +1,30 @@
[selene]
base = "lua51"
name = "vim"
[vim]
any = true
[jit]
any = true
[assert]
any = true
[describe]
any = true
[it]
any = true
[before_each]
any = true
[after_each]
any = true
[spy]
any = true
[stub]
any = true