initial commit
This commit is contained in:
commit
4c257d5065
9 changed files with 493 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal 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
64
README.md
Normal 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
1
colors/midnight.lua
Normal file
|
|
@ -0,0 +1 @@
|
|||
require('midnight').load()
|
||||
24
lua/midnight/init.lua
Normal file
24
lua/midnight/init.lua
Normal 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
96
lua/midnight/palette.lua
Normal 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
248
lua/midnight/theme.lua
Normal 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
1
selene.toml
Normal file
|
|
@ -0,0 +1 @@
|
|||
std = 'vim'
|
||||
8
stylua.toml
Normal file
8
stylua.toml
Normal 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
30
vim.toml
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue