feat(themes): add decoupled CSS theme system with lightningcss bundling

- Add 6 syntax highlighting themes (dracula, gruvbox, nord, github)
- Rewrite css.rs to use lightningcss bundler for @import resolution
- Theme CSS is inlined at build time, producing single bundled output
This commit is contained in:
Timothy DeHerrera
2026-02-05 12:19:47 -07:00
parent f9a978bdd6
commit caf2d506a7
10 changed files with 2083 additions and 172 deletions

124
docs/static/style.css vendored
View File

@@ -1,4 +1,6 @@
/* sukr docs — clean documentation theme */
@import "../../themes/default.css";
/* Syntax highlighting theme - lightningcss inlines this at build time */
:root {
--bg: #0d1117;
@@ -313,128 +315,6 @@ pre code {
padding: 0;
}
/* Syntax highlighting (tree-sitter classes)
* Dracula-inspired color scheme with full coverage
* All colors chosen to work well on dark backgrounds
*/
/* Keywords: control flow, declarations - Pink */
.hl-keyword {
color: #ff79c6;
}
/* Strings and literals - Yellow */
.hl-string,
.hl-string-special {
color: #f1fa8c;
}
.hl-string-escape {
color: #ffb86c;
}
.hl-string-special-path,
.hl-string-special-uri {
color: #8be9fd;
text-decoration: underline;
}
/* Functions and methods - Green */
.hl-function,
.hl-function-builtin {
color: #50fa7b;
}
/* Comments - Muted blue-gray */
.hl-comment {
color: #6272a4;
font-style: italic;
}
/* Numbers and constants - Purple */
.hl-number,
.hl-constant,
.hl-constant-builtin {
color: #bd93f9;
}
/* Operators - Pink (same as keywords for visual consistency) */
.hl-operator {
color: #ff79c6;
}
/* Punctuation - Subtle muted */
.hl-punctuation,
.hl-punctuation-bracket,
.hl-punctuation-delimiter {
color: var(--fg-muted);
}
.hl-punctuation-special {
color: #ff79c6;
}
/* Types - Cyan */
.hl-type,
.hl-type-builtin {
color: #8be9fd;
}
/* Variables - Light foreground */
.hl-variable {
color: var(--fg);
}
.hl-variable-builtin {
color: #ffb86c;
}
.hl-variable-parameter {
color: #ffb86c;
font-style: italic;
}
/* Properties and attributes - Cyan */
.hl-property,
.hl-attribute {
color: #8be9fd;
}
/* Constructors - Green */
.hl-constructor {
color: #50fa7b;
}
/* HTML/XML tags - Pink */
.hl-tag {
color: #ff79c6;
}
/* Embedded/escape content - Orange */
.hl-embedded,
.hl-escape {
color: #ffb86c;
}
/* Markdown-specific text highlighting */
.hl-text-title {
color: #ff79c6;
font-weight: bold;
}
.hl-text-literal {
color: #8be9fd;
}
.hl-text-uri {
color: #8be9fd;
text-decoration: underline;
}
.hl-text-reference {
color: #bd93f9;
}
/* Mermaid diagrams */
.mermaid-diagram {
margin: 1.5rem 0;

View File

@@ -1,31 +1,38 @@
//! CSS processing via lightningcss.
//!
//! Provides CSS bundling and minification. The bundler resolves `@import`
//! rules at build time, inlining imported files into a single output.
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet};
use lightningcss::bundler::{Bundler, FileProvider};
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions};
use std::path::Path;
/// Minify CSS content.
/// Bundle and minify a CSS file, resolving all `@import` rules.
///
/// Returns minified CSS string on success, or the original input on error.
pub fn minify_css(css: &str) -> String {
match try_minify(css) {
Ok(minified) => minified,
Err(_) => css.to_string(),
}
}
/// This function:
/// 1. Reads the CSS file at `path`
/// 2. Resolves and inlines all `@import` rules (relative to source file)
/// 3. Minifies the combined output
///
/// Returns minified CSS string on success, or an error message on failure.
pub fn bundle_css(path: &Path) -> Result<String, String> {
let fs = FileProvider::new();
let mut bundler = Bundler::new(&fs, None, ParserOptions::default());
fn try_minify(css: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut stylesheet = StyleSheet::parse(css, ParserOptions::default())
.map_err(|e| format!("parse error: {:?}", e))?;
let mut stylesheet = bundler
.bundle(path)
.map_err(|e| format!("bundle error: {e}"))?;
stylesheet
.minify(MinifyOptions::default())
.map_err(|e| format!("minify error: {:?}", e))?;
.map_err(|e| format!("minify error: {e}"))?;
let result = stylesheet
.to_css(PrinterOptions {
minify: true,
..Default::default()
})
.map_err(|e| format!("print error: {:?}", e))?;
.map_err(|e| format!("print error: {e}"))?;
Ok(result.code)
}
@@ -33,48 +40,89 @@ fn try_minify(css: &str) -> Result<String, Box<dyn std::error::Error>> {
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_minify_removes_whitespace() {
let input = r#"
fn test_bundle_minifies() {
let dir = TempDir::new().unwrap();
let css_path = dir.path().join("test.css");
fs::write(
&css_path,
r#"
.foo {
color: red;
}
"#;
let output = minify_css(input);
"#,
)
.unwrap();
// Should be smaller (whitespace removed)
assert!(output.len() < input.len());
// Should still contain the essential content
let output = bundle_css(&css_path).unwrap();
// Should be minified (whitespace removed)
assert!(output.contains(".foo"));
assert!(output.contains("color"));
assert!(output.contains("red"));
assert!(!output.contains('\n'));
}
#[test]
fn test_minify_removes_comments() {
let input = r#"
fn test_bundle_resolves_imports() {
let dir = TempDir::new().unwrap();
// Create imported file
let imported_path = dir.path().join("colors.css");
fs::write(
&imported_path,
r#"
:root {
--primary: blue;
}
"#,
)
.unwrap();
// Create main file that imports colors.css
let main_path = dir.path().join("main.css");
fs::write(
&main_path,
r#"
@import "colors.css";
.btn {
color: var(--primary);
}
"#,
)
.unwrap();
let output = bundle_css(&main_path).unwrap();
// Should contain content from both files
assert!(output.contains("--primary"));
assert!(output.contains("blue"));
assert!(output.contains(".btn"));
// Should NOT contain @import directive
assert!(!output.contains("@import"));
}
#[test]
fn test_bundle_removes_comments() {
let dir = TempDir::new().unwrap();
let css_path = dir.path().join("test.css");
fs::write(
&css_path,
r#"
/* This is a comment */
.bar { background: blue; }
"#;
let output = minify_css(input);
"#,
)
.unwrap();
let output = bundle_css(&css_path).unwrap();
// Comment should be removed
assert!(!output.contains("This is a comment"));
// Rule should remain
assert!(output.contains(".bar"));
}
#[test]
fn test_minify_merges_selectors() {
let input = r#"
.foo { color: red; }
.bar { color: red; }
"#;
let output = minify_css(input);
// Should merge identical rules
// Either ".foo,.bar" or ".bar,.foo" pattern
assert!(output.contains(","));
}
}

View File

@@ -52,6 +52,10 @@ pub enum Error {
#[source]
source: tera::Error,
},
/// Failed to bundle CSS.
#[error("CSS bundle error: {0}")]
CssBundle(String),
}
/// Result type alias for compiler operations.

View File

@@ -338,7 +338,7 @@ fn write_output(
/// Copy static assets (CSS, images, etc.) to output directory.
/// CSS files are minified before writing.
fn copy_static_assets(static_dir: &Path, output_dir: &Path) -> Result<()> {
use crate::css::minify_css;
use crate::css::bundle_css;
if !static_dir.exists() {
return Ok(()); // No static dir is fine
@@ -365,23 +365,20 @@ fn copy_static_assets(static_dir: &Path, output_dir: &Path) -> Result<()> {
})?;
}
// Minify CSS files, copy others directly
// Bundle CSS files (resolves @imports), copy others directly
if src.extension().is_some_and(|ext| ext == "css") {
let css = fs::read_to_string(src).map_err(|e| Error::ReadFile {
path: src.to_path_buf(),
source: e,
})?;
let minified = minify_css(&css);
fs::write(&dest, &minified).map_err(|e| Error::WriteFile {
let original_size = fs::metadata(src).map(|m| m.len()).unwrap_or(0);
let bundled = bundle_css(src).map_err(Error::CssBundle)?;
fs::write(&dest, &bundled).map_err(|e| Error::WriteFile {
path: dest.clone(),
source: e,
})?;
eprintln!(
"minifying: {}{} ({}{} bytes)",
"bundling: {}{} ({}{} bytes)",
src.display(),
dest.display(),
css.len(),
minified.len()
original_size,
bundled.len()
);
} else {
fs::copy(src, &dest).map_err(|e| Error::WriteFile {

282
themes/default.css Normal file
View File

@@ -0,0 +1,282 @@
/* Sukr Default Theme (Dracula-inspired)
* Syntax highlighting for code blocks
*
* Use with: @import 'themes/default.css';
* Or copy to your site's static/style.css
*/
:root {
/* Dracula Palette */
--hl-bg: #282a36;
--hl-fg: #f8f8f2;
--hl-comment: #6272a4;
--hl-cyan: #8be9fd;
--hl-green: #50fa7b;
--hl-orange: #ffb86c;
--hl-pink: #ff79c6;
--hl-purple: #bd93f9;
--hl-red: #ff5555;
--hl-yellow: #f1fa8c;
}
/* Keywords */
.hl-keyword {
color: var(--hl-pink);
}
.hl-keyword-control {
color: var(--hl-pink);
}
.hl-keyword-control-conditional {
color: var(--hl-pink);
}
.hl-keyword-control-repeat {
color: var(--hl-pink);
}
.hl-keyword-control-import {
color: var(--hl-pink);
}
.hl-keyword-control-return {
color: var(--hl-pink);
}
.hl-keyword-control-exception {
color: var(--hl-purple);
}
.hl-keyword-operator {
color: var(--hl-pink);
}
.hl-keyword-directive {
color: var(--hl-green);
}
.hl-keyword-function {
color: var(--hl-pink);
}
.hl-keyword-return {
color: var(--hl-pink);
}
.hl-keyword-storage {
color: var(--hl-pink);
}
.hl-keyword-storage-type {
color: var(--hl-cyan);
font-style: italic;
}
.hl-keyword-storage-modifier {
color: var(--hl-pink);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-pink);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-pink);
}
.hl-keyword-special {
color: var(--hl-pink);
}
/* Functions */
.hl-function {
color: var(--hl-green);
}
.hl-function-builtin {
color: var(--hl-green);
}
.hl-function-call {
color: var(--hl-green);
}
.hl-function-macro {
color: var(--hl-purple);
}
.hl-function-method {
color: var(--hl-green);
}
/* Types */
.hl-type {
color: var(--hl-cyan);
font-style: italic;
}
.hl-type-builtin {
color: var(--hl-cyan);
}
.hl-type-parameter {
color: var(--hl-cyan);
font-style: italic;
}
.hl-type-enum-variant {
color: var(--hl-fg);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-fg);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-purple);
}
.hl-constant-builtin {
color: var(--hl-purple);
}
.hl-constant-builtin-boolean {
color: var(--hl-purple);
}
.hl-constant-character {
color: var(--hl-cyan);
}
.hl-constant-character-escape {
color: var(--hl-pink);
}
.hl-constant-macro {
color: var(--hl-purple);
}
.hl-constant-numeric {
color: var(--hl-purple);
}
.hl-constant-numeric-integer {
color: var(--hl-purple);
}
.hl-constant-numeric-float {
color: var(--hl-purple);
}
/* Strings */
.hl-string {
color: var(--hl-yellow);
}
.hl-string-regexp {
color: var(--hl-red);
}
.hl-string-special {
color: var(--hl-orange);
}
.hl-string-special-path {
color: var(--hl-orange);
}
.hl-string-special-symbol {
color: var(--hl-yellow);
}
/* Variables */
.hl-variable {
color: var(--hl-fg);
}
.hl-variable-builtin {
color: var(--hl-purple);
font-style: italic;
}
.hl-variable-parameter {
color: var(--hl-orange);
font-style: italic;
}
.hl-variable-other {
color: var(--hl-fg);
}
.hl-variable-other-member {
color: var(--hl-fg);
}
/* Comments */
.hl-comment {
color: var(--hl-comment);
}
.hl-comment-line {
color: var(--hl-comment);
}
.hl-comment-block {
color: var(--hl-comment);
}
.hl-comment-block-documentation {
color: var(--hl-comment);
}
.hl-comment-line-documentation {
color: var(--hl-comment);
}
.hl-comment-unused {
color: var(--hl-comment);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-fg);
}
.hl-punctuation-bracket {
color: var(--hl-fg);
}
.hl-punctuation-delimiter {
color: var(--hl-fg);
}
.hl-punctuation-special {
color: var(--hl-pink);
}
/* Operators */
.hl-operator {
color: var(--hl-pink);
}
/* Other */
.hl-attribute {
color: var(--hl-green);
font-style: italic;
}
.hl-label {
color: var(--hl-cyan);
}
.hl-namespace {
color: var(--hl-fg);
}
.hl-constructor {
color: var(--hl-purple);
}
.hl-special {
color: var(--hl-pink);
}
.hl-tag {
color: var(--hl-pink);
}
.hl-tag-attribute {
color: var(--hl-purple);
}
.hl-tag-delimiter {
color: var(--hl-fg);
}
/* Markup (Markdown) */
.hl-markup-bold {
color: var(--hl-orange);
font-weight: bold;
}
.hl-markup-italic {
color: var(--hl-yellow);
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-purple);
font-weight: bold;
}
.hl-markup-link-text {
color: var(--hl-pink);
}
.hl-markup-link-url {
color: var(--hl-cyan);
}
.hl-markup-list {
color: var(--hl-cyan);
}
.hl-markup-quote {
color: var(--hl-yellow);
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-fg);
}
/* Unknown scope fallback */
.hl-unknown {
color: var(--hl-fg);
}

341
themes/dracula.css Normal file
View File

@@ -0,0 +1,341 @@
/* Dracula Theme for Sukr
* Based on https://draculatheme.com/
* Ported from Helix editor theme
*/
:root {
--hl-bg: #282a36;
--hl-fg: #f8f8f2;
--hl-comment: #6272a4;
--hl-cyan: #8be9fd;
--hl-green: #50fa7b;
--hl-orange: #ffb86c;
--hl-pink: #ff79c6;
--hl-purple: #bd93f9;
--hl-red: #ff5555;
--hl-yellow: #f1fa8c;
}
/* Keywords */
.hl-keyword {
color: var(--hl-pink);
}
.hl-keyword-control {
color: var(--hl-pink);
}
.hl-keyword-control-conditional {
color: var(--hl-pink);
}
.hl-keyword-control-repeat {
color: var(--hl-pink);
}
.hl-keyword-control-import {
color: var(--hl-pink);
}
.hl-keyword-control-return {
color: var(--hl-pink);
}
.hl-keyword-control-exception {
color: var(--hl-purple);
}
.hl-keyword-operator {
color: var(--hl-pink);
}
.hl-keyword-directive {
color: var(--hl-green);
}
.hl-keyword-function {
color: var(--hl-pink);
}
.hl-keyword-return {
color: var(--hl-pink);
}
.hl-keyword-storage {
color: var(--hl-pink);
}
.hl-keyword-storage-type {
color: var(--hl-cyan);
font-style: italic;
}
.hl-keyword-storage-modifier {
color: var(--hl-pink);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-pink);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-pink);
}
.hl-keyword-special {
color: var(--hl-pink);
}
/* Functions */
.hl-function {
color: var(--hl-green);
}
.hl-function-builtin {
color: var(--hl-green);
}
.hl-function-call {
color: var(--hl-green);
}
.hl-function-macro {
color: var(--hl-purple);
}
.hl-function-method {
color: var(--hl-green);
}
/* Types */
.hl-type {
color: var(--hl-cyan);
font-style: italic;
}
.hl-type-builtin {
color: var(--hl-cyan);
}
.hl-type-parameter {
color: var(--hl-cyan);
font-style: italic;
}
.hl-type-enum-variant {
color: var(--hl-fg);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-fg);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-purple);
}
.hl-constant-builtin {
color: var(--hl-purple);
}
.hl-constant-builtin-boolean {
color: var(--hl-purple);
}
.hl-constant-character {
color: var(--hl-cyan);
}
.hl-constant-character-escape {
color: var(--hl-pink);
}
.hl-constant-macro {
color: var(--hl-purple);
}
.hl-constant-numeric {
color: var(--hl-purple);
}
.hl-constant-numeric-integer {
color: var(--hl-purple);
}
.hl-constant-numeric-float {
color: var(--hl-purple);
}
/* Strings */
.hl-string {
color: var(--hl-yellow);
}
.hl-string-regexp {
color: var(--hl-red);
}
.hl-string-special {
color: var(--hl-orange);
}
.hl-string-special-path {
color: var(--hl-orange);
}
.hl-string-special-symbol {
color: var(--hl-yellow);
}
/* Variables */
.hl-variable {
color: var(--hl-fg);
}
.hl-variable-builtin {
color: var(--hl-purple);
font-style: italic;
}
.hl-variable-parameter {
color: var(--hl-orange);
font-style: italic;
}
.hl-variable-other {
color: var(--hl-fg);
}
.hl-variable-other-member {
color: var(--hl-fg);
}
/* Comments */
.hl-comment {
color: var(--hl-comment);
}
.hl-comment-line {
color: var(--hl-comment);
}
.hl-comment-block {
color: var(--hl-comment);
}
.hl-comment-block-documentation {
color: var(--hl-comment);
}
.hl-comment-line-documentation {
color: var(--hl-comment);
}
.hl-comment-unused {
color: var(--hl-comment);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-fg);
}
.hl-punctuation-bracket {
color: var(--hl-fg);
}
.hl-punctuation-delimiter {
color: var(--hl-fg);
}
.hl-punctuation-special {
color: var(--hl-pink);
}
/* Operators */
.hl-operator {
color: var(--hl-pink);
}
/* Other */
.hl-attribute {
color: var(--hl-green);
font-style: italic;
}
.hl-label {
color: var(--hl-cyan);
}
.hl-namespace {
color: var(--hl-fg);
}
.hl-constructor {
color: var(--hl-purple);
}
.hl-special {
color: var(--hl-pink);
}
.hl-tag {
color: var(--hl-pink);
}
.hl-tag-attribute {
color: var(--hl-purple);
}
.hl-tag-delimiter {
color: var(--hl-fg);
}
/* Markup */
.hl-markup-bold {
color: var(--hl-orange);
font-weight: bold;
}
.hl-markup-italic {
color: var(--hl-yellow);
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-purple);
font-weight: bold;
}
.hl-markup-link-text {
color: var(--hl-pink);
}
.hl-markup-link-url {
color: var(--hl-cyan);
}
.hl-markup-list {
color: var(--hl-cyan);
}
.hl-markup-quote {
color: var(--hl-yellow);
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-fg);
}
.hl-unknown {
color: var(--hl-fg);
}

332
themes/github_dark.css Normal file
View File

@@ -0,0 +1,332 @@
/* GitHub Dark Theme for Sukr
* Based on https://primer.style/primitives/colors
* Ported from Helix editor theme
*/
:root {
/* GitHub Dark Palette */
--hl-bg: #0d1117;
--hl-fg: #c9d1d9;
--hl-muted: #8b949e;
--hl-red: #ff7b72;
--hl-orange: #ffa657;
--hl-blue-light: #a5d6ff;
--hl-blue: #79c0ff;
--hl-purple: #d2a8ff;
--hl-green: #7ee787;
}
/* Keywords */
.hl-keyword {
color: var(--hl-red);
}
.hl-keyword-control {
color: var(--hl-red);
}
.hl-keyword-control-conditional {
color: var(--hl-red);
}
.hl-keyword-control-repeat {
color: var(--hl-red);
}
.hl-keyword-control-import {
color: var(--hl-red);
}
.hl-keyword-control-return {
color: var(--hl-red);
}
.hl-keyword-control-exception {
color: var(--hl-red);
}
.hl-keyword-operator {
color: var(--hl-red);
}
.hl-keyword-directive {
color: var(--hl-red);
}
.hl-keyword-function {
color: var(--hl-red);
}
.hl-keyword-return {
color: var(--hl-red);
}
.hl-keyword-storage {
color: var(--hl-red);
}
.hl-keyword-storage-type {
color: var(--hl-blue);
}
.hl-keyword-storage-modifier {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-red);
}
.hl-keyword-special {
color: var(--hl-red);
}
/* Functions */
.hl-function {
color: var(--hl-purple);
}
.hl-function-builtin {
color: var(--hl-purple);
}
.hl-function-call {
color: var(--hl-purple);
}
.hl-function-macro {
color: var(--hl-purple);
}
.hl-function-method {
color: var(--hl-purple);
}
/* Types */
.hl-type {
color: var(--hl-orange);
}
.hl-type-builtin {
color: var(--hl-blue);
}
.hl-type-parameter {
color: var(--hl-orange);
}
.hl-type-enum-variant {
color: var(--hl-fg);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-fg);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-blue);
}
.hl-constant-builtin {
color: var(--hl-blue);
}
.hl-constant-builtin-boolean {
color: var(--hl-blue);
}
.hl-constant-character {
color: var(--hl-blue);
}
.hl-constant-character-escape {
color: var(--hl-blue);
}
.hl-constant-macro {
color: var(--hl-blue);
}
.hl-constant-numeric {
color: var(--hl-blue);
}
.hl-constant-numeric-integer {
color: var(--hl-blue);
}
.hl-constant-numeric-float {
color: var(--hl-blue);
}
/* Strings */
.hl-string {
color: var(--hl-blue-light);
}
.hl-string-regexp {
color: var(--hl-blue-light);
}
.hl-string-special {
color: var(--hl-blue-light);
}
.hl-string-special-path {
color: var(--hl-blue-light);
}
.hl-string-special-symbol {
color: var(--hl-blue-light);
}
/* Variables */
.hl-variable {
color: var(--hl-fg);
}
.hl-variable-builtin {
color: var(--hl-red);
}
.hl-variable-parameter {
color: var(--hl-orange);
}
.hl-variable-other {
color: var(--hl-fg);
}
.hl-variable-other-member {
color: var(--hl-blue-light);
}
/* Comments */
.hl-comment {
color: var(--hl-muted);
}
.hl-comment-line {
color: var(--hl-muted);
}
.hl-comment-block {
color: var(--hl-muted);
}
.hl-comment-block-documentation {
color: var(--hl-muted);
}
.hl-comment-line-documentation {
color: var(--hl-muted);
}
.hl-comment-unused {
color: var(--hl-muted);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-fg);
}
.hl-punctuation-bracket {
color: var(--hl-fg);
}
.hl-punctuation-delimiter {
color: var(--hl-fg);
}
.hl-punctuation-special {
color: var(--hl-blue-light);
}
/* Operators */
.hl-operator {
color: var(--hl-blue-light);
}
/* Other */
.hl-attribute {
color: var(--hl-fg);
}
.hl-label {
color: var(--hl-red);
}
.hl-namespace {
color: var(--hl-orange);
}
.hl-constructor {
color: var(--hl-purple);
}
.hl-special {
color: var(--hl-blue-light);
}
.hl-tag {
color: var(--hl-green);
}
.hl-tag-attribute {
color: var(--hl-purple);
}
.hl-tag-delimiter {
color: var(--hl-fg);
}
/* Markup */
.hl-markup-bold {
font-weight: bold;
}
.hl-markup-italic {
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-blue);
}
.hl-markup-link-text {
color: var(--hl-blue-light);
text-decoration: underline;
}
.hl-markup-link-url {
text-decoration: underline;
}
.hl-markup-list {
color: var(--hl-red);
}
.hl-markup-quote {
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-blue);
}
.hl-unknown {
color: var(--hl-fg);
}

332
themes/github_light.css Normal file
View File

@@ -0,0 +1,332 @@
/* GitHub Light Theme for Sukr
* Based on https://primer.style/primitives/colors
* Ported from Helix editor theme
*/
:root {
/* GitHub Light Palette */
--hl-bg: #ffffff;
--hl-fg: #24292f;
--hl-muted: #57606a;
--hl-red: #cf222e;
--hl-orange: #953800;
--hl-blue: #0550ae;
--hl-blue-dark: #0a3069;
--hl-purple: #8250df;
--hl-green: #116329;
}
/* Keywords */
.hl-keyword {
color: var(--hl-red);
}
.hl-keyword-control {
color: var(--hl-red);
}
.hl-keyword-control-conditional {
color: var(--hl-red);
}
.hl-keyword-control-repeat {
color: var(--hl-red);
}
.hl-keyword-control-import {
color: var(--hl-red);
}
.hl-keyword-control-return {
color: var(--hl-red);
}
.hl-keyword-control-exception {
color: var(--hl-red);
}
.hl-keyword-operator {
color: var(--hl-red);
}
.hl-keyword-directive {
color: var(--hl-red);
}
.hl-keyword-function {
color: var(--hl-red);
}
.hl-keyword-return {
color: var(--hl-red);
}
.hl-keyword-storage {
color: var(--hl-red);
}
.hl-keyword-storage-type {
color: var(--hl-blue);
}
.hl-keyword-storage-modifier {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-red);
}
.hl-keyword-special {
color: var(--hl-red);
}
/* Functions */
.hl-function {
color: var(--hl-purple);
}
.hl-function-builtin {
color: var(--hl-purple);
}
.hl-function-call {
color: var(--hl-purple);
}
.hl-function-macro {
color: var(--hl-purple);
}
.hl-function-method {
color: var(--hl-purple);
}
/* Types */
.hl-type {
color: var(--hl-orange);
}
.hl-type-builtin {
color: var(--hl-blue);
}
.hl-type-parameter {
color: var(--hl-orange);
}
.hl-type-enum-variant {
color: var(--hl-fg);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-fg);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-blue);
}
.hl-constant-builtin {
color: var(--hl-blue);
}
.hl-constant-builtin-boolean {
color: var(--hl-blue);
}
.hl-constant-character {
color: var(--hl-blue);
}
.hl-constant-character-escape {
color: var(--hl-blue);
}
.hl-constant-macro {
color: var(--hl-blue);
}
.hl-constant-numeric {
color: var(--hl-blue);
}
.hl-constant-numeric-integer {
color: var(--hl-blue);
}
.hl-constant-numeric-float {
color: var(--hl-blue);
}
/* Strings */
.hl-string {
color: var(--hl-blue-dark);
}
.hl-string-regexp {
color: var(--hl-blue-dark);
}
.hl-string-special {
color: var(--hl-blue-dark);
}
.hl-string-special-path {
color: var(--hl-blue-dark);
}
.hl-string-special-symbol {
color: var(--hl-blue-dark);
}
/* Variables */
.hl-variable {
color: var(--hl-fg);
}
.hl-variable-builtin {
color: var(--hl-red);
}
.hl-variable-parameter {
color: var(--hl-orange);
}
.hl-variable-other {
color: var(--hl-fg);
}
.hl-variable-other-member {
color: var(--hl-blue-dark);
}
/* Comments */
.hl-comment {
color: var(--hl-muted);
}
.hl-comment-line {
color: var(--hl-muted);
}
.hl-comment-block {
color: var(--hl-muted);
}
.hl-comment-block-documentation {
color: var(--hl-muted);
}
.hl-comment-line-documentation {
color: var(--hl-muted);
}
.hl-comment-unused {
color: var(--hl-muted);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-fg);
}
.hl-punctuation-bracket {
color: var(--hl-fg);
}
.hl-punctuation-delimiter {
color: var(--hl-fg);
}
.hl-punctuation-special {
color: var(--hl-blue-dark);
}
/* Operators */
.hl-operator {
color: var(--hl-blue-dark);
}
/* Other */
.hl-attribute {
color: var(--hl-fg);
}
.hl-label {
color: var(--hl-red);
}
.hl-namespace {
color: var(--hl-orange);
}
.hl-constructor {
color: var(--hl-purple);
}
.hl-special {
color: var(--hl-blue-dark);
}
.hl-tag {
color: var(--hl-green);
}
.hl-tag-attribute {
color: var(--hl-purple);
}
.hl-tag-delimiter {
color: var(--hl-fg);
}
/* Markup */
.hl-markup-bold {
font-weight: bold;
}
.hl-markup-italic {
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-blue);
}
.hl-markup-link-text {
color: var(--hl-blue-dark);
text-decoration: underline;
}
.hl-markup-link-url {
text-decoration: underline;
}
.hl-markup-list {
color: var(--hl-red);
}
.hl-markup-quote {
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-blue);
}
.hl-unknown {
color: var(--hl-fg);
}

340
themes/gruvbox.css Normal file
View File

@@ -0,0 +1,340 @@
/* Gruvbox Theme for Sukr
* Based on https://github.com/morhetz/gruvbox
* Ported from Helix editor theme
*/
:root {
--hl-bg: #282828;
--hl-fg: #ebdbb2;
--hl-gray: #928374;
--hl-red: #fb4934;
--hl-green: #b8bb26;
--hl-yellow: #fabd2f;
--hl-blue: #83a598;
--hl-purple: #d3869b;
--hl-aqua: #8ec07c;
--hl-orange: #fe8019;
}
/* Keywords */
.hl-keyword {
color: var(--hl-red);
}
.hl-keyword-control {
color: var(--hl-red);
}
.hl-keyword-control-conditional {
color: var(--hl-red);
}
.hl-keyword-control-repeat {
color: var(--hl-red);
}
.hl-keyword-control-import {
color: var(--hl-aqua);
}
.hl-keyword-control-return {
color: var(--hl-red);
}
.hl-keyword-control-exception {
color: var(--hl-red);
}
.hl-keyword-operator {
color: var(--hl-red);
}
.hl-keyword-directive {
color: var(--hl-aqua);
}
.hl-keyword-function {
color: var(--hl-red);
}
.hl-keyword-return {
color: var(--hl-red);
}
.hl-keyword-storage {
color: var(--hl-red);
}
.hl-keyword-storage-type {
color: var(--hl-yellow);
}
.hl-keyword-storage-modifier {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-red);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-red);
}
.hl-keyword-special {
color: var(--hl-red);
}
/* Functions */
.hl-function {
color: var(--hl-green);
}
.hl-function-builtin {
color: var(--hl-yellow);
}
.hl-function-call {
color: var(--hl-green);
}
.hl-function-macro {
color: var(--hl-blue);
}
.hl-function-method {
color: var(--hl-green);
}
/* Types */
.hl-type {
color: var(--hl-yellow);
}
.hl-type-builtin {
color: var(--hl-yellow);
}
.hl-type-parameter {
color: var(--hl-yellow);
}
.hl-type-enum-variant {
color: var(--hl-fg);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-fg);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-purple);
}
.hl-constant-builtin {
color: var(--hl-purple);
}
.hl-constant-builtin-boolean {
color: var(--hl-purple);
}
.hl-constant-character {
color: var(--hl-aqua);
}
.hl-constant-character-escape {
color: var(--hl-orange);
}
.hl-constant-macro {
color: var(--hl-aqua);
}
.hl-constant-numeric {
color: var(--hl-purple);
}
.hl-constant-numeric-integer {
color: var(--hl-purple);
}
.hl-constant-numeric-float {
color: var(--hl-purple);
}
/* Strings */
.hl-string {
color: var(--hl-green);
}
.hl-string-regexp {
color: var(--hl-orange);
}
.hl-string-special {
color: var(--hl-orange);
}
.hl-string-special-path {
color: var(--hl-orange);
}
.hl-string-special-symbol {
color: var(--hl-yellow);
}
/* Variables */
.hl-variable {
color: var(--hl-fg);
}
.hl-variable-builtin {
color: var(--hl-orange);
font-style: italic;
}
.hl-variable-parameter {
color: var(--hl-blue);
font-style: italic;
}
.hl-variable-other {
color: var(--hl-fg);
}
.hl-variable-other-member {
color: var(--hl-blue);
}
/* Comments */
.hl-comment {
color: var(--hl-gray);
font-style: italic;
}
.hl-comment-line {
color: var(--hl-gray);
font-style: italic;
}
.hl-comment-block {
color: var(--hl-gray);
font-style: italic;
}
.hl-comment-block-documentation {
color: var(--hl-gray);
font-style: italic;
}
.hl-comment-line-documentation {
color: var(--hl-gray);
font-style: italic;
}
.hl-comment-unused {
color: var(--hl-gray);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-orange);
}
.hl-punctuation-bracket {
color: var(--hl-orange);
}
.hl-punctuation-delimiter {
color: var(--hl-orange);
}
.hl-punctuation-special {
color: var(--hl-orange);
}
/* Operators */
.hl-operator {
color: var(--hl-purple);
}
/* Other */
.hl-attribute {
color: var(--hl-aqua);
font-style: italic;
}
.hl-label {
color: var(--hl-red);
}
.hl-namespace {
color: var(--hl-fg);
}
.hl-constructor {
color: var(--hl-purple);
}
.hl-special {
color: var(--hl-purple);
}
.hl-tag {
color: var(--hl-aqua);
}
.hl-tag-attribute {
color: var(--hl-aqua);
}
.hl-tag-delimiter {
color: var(--hl-fg);
}
/* Markup */
.hl-markup-bold {
font-weight: bold;
}
.hl-markup-italic {
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-aqua);
}
.hl-markup-link-text {
color: var(--hl-red);
}
.hl-markup-link-url {
color: var(--hl-green);
text-decoration: underline;
}
.hl-markup-list {
color: var(--hl-aqua);
}
.hl-markup-quote {
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-red);
}
.hl-unknown {
color: var(--hl-fg);
}

355
themes/nord.css Normal file
View File

@@ -0,0 +1,355 @@
/* Nord Theme for Sukr
* Based on https://www.nordtheme.com/
* Ported from Helix editor theme
*/
:root {
/* Polar Night */
--hl-nord0: #2e3440;
--hl-nord1: #3b4252;
--hl-nord2: #434c5e;
--hl-nord3: #4c566a;
--hl-nord3-bright: #616e88;
/* Snow Storm */
--hl-nord4: #d8dee9;
--hl-nord5: #e5e9f0;
--hl-nord6: #eceff4;
/* Frost */
--hl-nord7: #8fbcbb;
--hl-nord8: #88c0d0;
--hl-nord9: #81a1c1;
--hl-nord10: #5e81ac;
/* Aurora */
--hl-nord11: #bf616a;
--hl-nord12: #d08770;
--hl-nord13: #ebcb8b;
--hl-nord14: #a3be8c;
--hl-nord15: #b48ead;
/* Semantic aliases */
--hl-bg: var(--hl-nord0);
--hl-fg: var(--hl-nord4);
--hl-comment: var(--hl-nord3-bright);
}
/* Keywords */
.hl-keyword {
color: var(--hl-nord9);
}
.hl-keyword-control {
color: var(--hl-nord9);
}
.hl-keyword-control-conditional {
color: var(--hl-nord9);
}
.hl-keyword-control-repeat {
color: var(--hl-nord9);
}
.hl-keyword-control-import {
color: var(--hl-nord9);
}
.hl-keyword-control-return {
color: var(--hl-nord9);
}
.hl-keyword-control-exception {
color: var(--hl-nord9);
}
.hl-keyword-operator {
color: var(--hl-nord9);
}
.hl-keyword-directive {
color: var(--hl-nord9);
}
.hl-keyword-function {
color: var(--hl-nord9);
}
.hl-keyword-return {
color: var(--hl-nord9);
}
.hl-keyword-storage {
color: var(--hl-nord9);
}
.hl-keyword-storage-type {
color: var(--hl-nord9);
}
.hl-keyword-storage-modifier {
color: var(--hl-nord9);
}
.hl-keyword-storage-modifier-mut {
color: var(--hl-nord9);
}
.hl-keyword-storage-modifier-ref {
color: var(--hl-nord9);
}
.hl-keyword-special {
color: var(--hl-nord9);
}
/* Functions */
.hl-function {
color: var(--hl-nord8);
}
.hl-function-builtin {
color: var(--hl-nord7);
}
.hl-function-call {
color: var(--hl-nord8);
}
.hl-function-macro {
color: var(--hl-nord9);
}
.hl-function-method {
color: var(--hl-nord8);
}
/* Types */
.hl-type {
color: var(--hl-nord7);
}
.hl-type-builtin {
color: var(--hl-nord7);
}
.hl-type-parameter {
color: var(--hl-nord7);
}
.hl-type-enum-variant {
color: var(--hl-nord4);
font-style: italic;
}
.hl-type-enum-variant-builtin {
color: var(--hl-nord4);
font-style: italic;
}
/* Constants */
.hl-constant {
color: var(--hl-nord4);
}
.hl-constant-builtin {
color: var(--hl-nord9);
}
.hl-constant-builtin-boolean {
color: var(--hl-nord9);
}
.hl-constant-character {
color: var(--hl-nord15);
}
.hl-constant-character-escape {
color: var(--hl-nord13);
}
.hl-constant-macro {
color: var(--hl-nord9);
}
.hl-constant-numeric {
color: var(--hl-nord15);
}
.hl-constant-numeric-integer {
color: var(--hl-nord15);
}
.hl-constant-numeric-float {
color: var(--hl-nord15);
}
/* Strings */
.hl-string {
color: var(--hl-nord14);
}
.hl-string-regexp {
color: var(--hl-nord13);
}
.hl-string-special {
color: var(--hl-nord13);
}
.hl-string-special-path {
color: var(--hl-nord13);
}
.hl-string-special-symbol {
color: var(--hl-nord13);
}
/* Variables */
.hl-variable {
color: var(--hl-nord4);
}
.hl-variable-builtin {
color: var(--hl-nord9);
}
.hl-variable-parameter {
color: var(--hl-nord8);
}
.hl-variable-other {
color: var(--hl-nord4);
}
.hl-variable-other-member {
color: var(--hl-nord4);
}
/* Comments */
.hl-comment {
color: var(--hl-nord3-bright);
font-style: italic;
}
.hl-comment-line {
color: var(--hl-nord3-bright);
font-style: italic;
}
.hl-comment-block {
color: var(--hl-nord3-bright);
font-style: italic;
}
.hl-comment-block-documentation {
color: var(--hl-nord3-bright);
font-style: italic;
}
.hl-comment-line-documentation {
color: var(--hl-nord3-bright);
font-style: italic;
}
.hl-comment-unused {
color: var(--hl-nord3-bright);
opacity: 0.6;
}
/* Punctuation */
.hl-punctuation {
color: var(--hl-nord6);
}
.hl-punctuation-bracket {
color: var(--hl-nord6);
}
.hl-punctuation-delimiter {
color: var(--hl-nord6);
}
.hl-punctuation-special {
color: var(--hl-nord9);
}
/* Operators */
.hl-operator {
color: var(--hl-nord9);
}
/* Other */
.hl-attribute {
color: var(--hl-nord9);
}
.hl-label {
color: var(--hl-nord7);
}
.hl-namespace {
color: var(--hl-nord4);
}
.hl-constructor {
color: var(--hl-nord8);
}
.hl-special {
color: var(--hl-nord9);
}
.hl-tag {
color: var(--hl-nord7);
}
.hl-tag-attribute {
color: var(--hl-nord9);
}
.hl-tag-delimiter {
color: var(--hl-nord6);
}
/* Markup */
.hl-markup-bold {
font-weight: bold;
}
.hl-markup-italic {
font-style: italic;
}
.hl-markup-strikethrough {
text-decoration: line-through;
}
.hl-markup-heading {
color: var(--hl-nord8);
}
.hl-markup-link-text {
color: var(--hl-nord8);
}
.hl-markup-link-url {
color: var(--hl-nord9);
}
.hl-markup-list {
color: var(--hl-nord9);
}
.hl-markup-quote {
font-style: italic;
}
.hl-markup-raw {
color: var(--hl-nord7);
}
.hl-unknown {
color: var(--hl-nord4);
}