irvin.dev

Liminal Salt Theme

Liminal Salt is an LLM frontend for OpenRouter that I built in Python and Django, but it also spawned a color theme that I've been using for various projects. The overall aesthetic is inspired by greige, kind of a beige/sage/stone milieu. I've tried to make it accessible and comfortable for reading.

Liminal Salt has an earthy vibe that I really enjoy. I'm transitioning all my tools to use it. Check out the theme on GitHub: liminal-salt

It's still evolving, but here's the current palette:

Dark Theme

Base

The default page background and primary text colors. Foreground Secondary is for supporting text like subtitles and metadata.

#1a1c1b
Background
#e8e4dc
Foreground
#c5c1b8
Foreground Secondary

Muted

A subdued surface for secondary UI like disabled states, chips, and sidebars. Muted Foreground is for placeholder text and de-emphasized labels.

#141615
Muted
#9e9b93
Muted Foreground

Card

An elevated surface for cards, dialogs, popovers, and panels that sit above the background.

#242726
Card
#e8e4dc
Card Foreground

Accent

The primary action color for buttons, links, and interactive highlights. Accent Foreground is the text color on accent-colored surfaces.

#8fac98
Accent
#a3bfac
Accent Hover
#1a1c1b
Accent Foreground

Destructive

For destructive actions and error states — delete buttons, form validation errors, error toasts, and danger alerts.

#cc8585
Destructive
#d99292
Destructive Hover
#1a1c1b
Destructive Foreground

Success

For confirmations and positive states — saved indicators, valid form fields, success toasts, and completion badges.

#7dba8a
Success
#1a1c1b
Success Foreground

Warning

For caution states — warnings, deprecation notices, and attention-needed indicators that aren't errors.

#c9a86c
Warning
#1a1c1b
Warning Foreground

Borders & Focus

Structural and interactive borders. Ring is the focus indicator for keyboard navigation. Input is the default border for form fields. Border is for layout dividers and container edges.

#95bebe
Ring
#6b7369
Input
#2e312f
Border

Syntax

Colors for code highlighting — keywords, strings, types, and other language constructs.

#8fac98
Keyword
#a3bfac
Function
#c9a86c
String
#7eb8c9
Number
#8fb8ad
Type
#9e9b93
Comment
#cc8585
Tag
#a9ad78
Regex

Editor

Colors for editor chrome — cursor, selection, line highlighting, gutter, and diff backgrounds. These are the tinted surfaces used in code editors and IDEs.

#8fac98
Cursor
#3d4741
Selection
#393a38
Line Highlight
#4f4633
Find Match
#9e9b93
Gutter
#c5c1b8
Gutter Active
#2e312f
Bracket Match
#2e312f
Indent Guide
#4f3c3b
Diff Deleted
#384b3c
Diff Inserted

Light Theme

Base

#f5f2ed
Background
#2d2b28
Foreground
#5a5753
Foreground Secondary

Muted

#ebe7e0
Muted
#6b6762
Muted Foreground

Card

#fdfcfa
Card
#2d2b28
Card Foreground

Accent

#506e58
Accent
#425f4a
Accent Hover
#f5f2ed
Accent Foreground

Destructive

#a54d4d
Destructive
#984242
Destructive Hover
#f5f2ed
Destructive Foreground

Success

#3a7346
Success
#f5f2ed
Success Foreground

Warning

#7d6325
Warning
#f5f2ed
Warning Foreground

Borders & Focus

#3e5d5d
Ring
#888379
Input
#ddd8d0
Border

Syntax

#506e58
Keyword
#425f4a
Function
#7d6325
String
#3a6f80
Number
#3e6b5d
Type
#6b6762
Comment
#a54d4d
Tag
#5c6135
Regex

Editor

#506e58
Cursor
#c4cac0
Selection
#d7d4cf
Line Highlight
#d1c7b1
Find Match
#6b6762
Gutter
#5a5753
Gutter Active
#ddd8d0
Bracket Match
#ddd8d0
Indent Guide
#ddc1bd
Diff Deleted
#bdccbb
Diff Inserted

Accessibility

All foreground/background pairings meet WCAG 2.1 AA (4.5:1 for normal text, 3:1 for large text). Many primary pairings exceed AAA (7:1).

Dark Theme

On surfaces

Contrast ratios for text and UI elements on the three surface tokens.

Token Background Card Muted Level
Foreground 13.5 11.9 14.3 AAA
Foreground Secondary 9.5 8.4 10.1 AAA
Muted Foreground 6.2 5.4 6.5 AA
Accent 7.0 6.1 7.4 AA-AAA
Accent Hover 8.7 7.6 9.2 AAA
Destructive 5.9 5.2 6.3 AA
Destructive Hover 6.9 6.1 7.3 AA
Success 7.6 6.6 8.0 AA-AAA
Warning 7.6 6.7 8.1 AA-AAA
Ring 8.5 7.5 9.0 AAA
Input 3.5 3.1 3.7 AA (non-text)

Paired foregrounds

Contrast ratios for foreground tokens measured against their own surface.

Token Surface Contrast Level
Card Foreground Card 11.9 AAA
Accent Foreground Accent 7.0 AAA
Destructive Foreground Destructive 5.9 AA
Success Foreground Success 7.6 AAA
Warning Foreground Warning 7.6 AAA

Syntax on surfaces

Contrast ratios for syntax highlighting tokens on the three surface tokens.

Token Background Card Muted Level
Keyword 7.0 6.1 7.4 AA-AAA
Function 8.7 7.6 9.2 AAA
String 7.6 6.7 8.1 AA-AAA
Number 7.8 6.9 8.3 AA-AAA
Type 7.9 6.9 8.3 AA-AAA
Comment 6.2 5.4 6.5 AA
Tag 5.9 5.2 6.3 AA
Regex 7.3 6.4 7.7 AA-AAA

Editor chrome

Contrast ratios for editor text elements on background, and foreground text readability on tinted highlight surfaces.

Token On Background Level
Cursor 7.0 AAA
Gutter 6.2 AA
Gutter Active 9.5 AAA
Token Foreground on surface Level
Selection 7.6 AAA
Line Highlight 9.0 AAA
Find Match 7.3 AAA
Diff Deleted 8.1 AAA
Diff Inserted 7.4 AAA

Light Theme

On surfaces

Token Background Card Muted Level
Foreground 12.6 13.8 11.5 AAA
Foreground Secondary 6.4 7.0 5.8 AA-AAA
Muted Foreground 5.0 5.5 4.6 AA
Accent 5.1 5.5 4.6 AA
Accent Hover 6.3 6.9 5.7 AA
Destructive 5.0 5.4 4.5 AA
Destructive Hover 5.9 6.4 5.3 AA
Success 5.1 5.5 4.6 AA
Warning 5.1 5.6 4.6 AA
Ring 6.4 7.0 5.8 AA-AAA
Input 3.4 3.7 3.1 AA (non-text)

Paired foregrounds

Token Surface Contrast Level
Card Foreground Card 13.8 AAA
Accent Foreground Accent 5.1 AA
Destructive Foreground Destructive 5.0 AA
Success Foreground Success 5.1 AA
Warning Foreground Warning 5.1 AA

Syntax on surfaces

Token Background Card Muted Level
Keyword 5.1 5.5 4.6 AA
Function 6.3 6.9 5.7 AA
String 5.1 5.6 4.6 AA
Number 5.0 5.4 4.5 AA
Type 5.4 5.9 4.9 AA
Comment 5.0 5.5 4.6 AA
Tag 5.0 5.4 4.5 AA
Regex 5.8 6.4 5.3 AA

Editor chrome

Token On Background Level
Cursor 5.1 AA
Gutter 5.0 AA
Gutter Active 6.4 AA
Token Foreground on surface Level
Selection 8.4 AAA
Line Highlight 9.5 AAA
Find Match 8.4 AAA
Diff Deleted 8.4 AAA
Diff Inserted 8.4 AAA

Examples

Here's how the theme looks applied to common UI elements. Toggle light/dark mode to see both variants.

Buttons

Form Fields

Code Blocks

from dataclasses import dataclass

# Minimum contrast for WCAG AA compliance
CONTRAST_AA = 4.5

@dataclass
class Color:
    """A named color primitive."""
    name: str
    value: str
    luminance: float = 0.0

    def meets_aa(self, other: "Color") -> bool:
        ratio = self.contrast(other)
        return ratio >= CONTRAST_AA

palette = {"sage": "#8fac98", "amber": "#c9a86c"}
total = len(palette)
is_valid = total > 0 and True
print(f"Colors: {total}, valid={is_valid}")
<div class="swatch" data-color="#8fac98">
  <span>Sage</span>
  <code>#8fac98</code>
</div>
const HEX = /^#([a-f\d]{2}){3}$/i

function parseHex(value) {
  if (!HEX.test(value)) {
    throw new Error(`Invalid: ${value}`)
  }
  return [1, 3, 5].map(
    (i) => parseInt(value.slice(i, i + 2), 16)
  )
}

Blockquote

The Liminal Salt palette draws from natural, earthy tones — muted sage greens and warm beiges that feel grounded without being heavy.

Here's some inline code alongside a link to the repository. The theme's --accent variable drives both the link color and the code highlight, keeping everything visually cohesive across light and dark modes.