mirror of
https://github.com/xscriptor/obsidian.git
synced 2026-07-22 04:10:24 +00:00
start work with x theme
This commit is contained in:
parent
1dc71078bc
commit
e4bb00f844
7 changed files with 3582 additions and 416 deletions
67
README.md
67
README.md
|
|
@ -1,39 +1,64 @@
|
|||
# Xscriptor
|
||||
# x
|
||||
|
||||
An elegant Obsidian theme for coders and writers with beautiful EB Garamond typography and flexible customization.
|
||||
`x` is a theme variation focused on reading, writing, and editorial work inside Obsidian. It keeps EB Garamond as its typographic base, while replacing the original colorful palette with parchment, ink, wine, bronze, and sage tones.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
- Elegant EB Garamond typography
|
||||
- Light & dark color palettes
|
||||
- Subtle transparency and frosted-glass effects
|
||||
- Enhanced code block readability
|
||||
- Style Settings integration for customization
|
||||
## Focus
|
||||
- A more restrained, literary palette for long reading sessions
|
||||
- Warm contrast in both light and dark mode
|
||||
- Code blocks that feel less neon and closer to ink on paper
|
||||
- Folder accents that are muted instead of highly saturated
|
||||
- Compatibility with the existing Style Settings and pseudo mica effect
|
||||
|
||||
## Installation
|
||||
- From Obsidian: Settings → Appearance → Community Themes → Search **Xscriptor**
|
||||
- Or manually: copy the `files` into `.obsidian/themes/Xscriptor/` and select it in Settings → Appearance
|
||||
## How Obsidian Themes Work
|
||||
- Obsidian loads themes from a folder inside `.obsidian/themes/`
|
||||
- An app theme requires at minimum `theme.css` and `manifest.json`
|
||||
- `theme.css` contains CSS variables and visual rules for the interface
|
||||
- `manifest.json` defines the theme name, version, author, and minimum Obsidian version
|
||||
- `versions.json` maps theme versions to compatible Obsidian versions when you publish releases
|
||||
|
||||
## Repository Structure
|
||||
- `theme.css`: palette, typography, components, and settings exposed to Style Settings
|
||||
- `manifest.json`: theme metadata
|
||||
- `versions.json`: version compatibility
|
||||
- `docs/preview/`: reference screenshots
|
||||
|
||||
## Changes In This Iteration
|
||||
- The documented variant was renamed to `x`
|
||||
- Base variables were adjusted in `:root`, `.theme-light`, and `.theme-dark`
|
||||
- Bright accents were replaced with a warm editorial palette
|
||||
- File explorer folder colors were softened
|
||||
- The color treatment for inline code, code blocks, and tokens was reworked
|
||||
|
||||
## Manual Installation
|
||||
- Copy this repository into `.obsidian/themes/`
|
||||
- Select the theme from `Settings -> Appearance`
|
||||
- If you plan to publish this variant as `x`, also align the folder name and `manifest.json` with that name, since Obsidian expects them to match
|
||||
|
||||
## Previews
|
||||
- Mobile
|
||||
<p align="center">
|
||||
<img src="./docs/preview/preview02.jpg" width="200" alt="Obsidian Xscriptor Mobile Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview03.jpg" width="200" alt="Obsidian Xscriptor Mobile Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview04.jpg" width="200" alt="Obsidian Xscriptor Mobile Theme Light Mode"/>
|
||||
<img src="./docs/preview/preview05.jpg" width="200" alt="Obsidian Xscriptor Mobile Theme Light Mode"/>
|
||||
<img src="./docs/preview/preview02.jpg" width="200" alt="Obsidian x Mobile Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview03.jpg" width="200" alt="Obsidian x Mobile Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview04.jpg" width="200" alt="Obsidian x Mobile Theme Light Mode"/>
|
||||
<img src="./docs/preview/preview05.jpg" width="200" alt="Obsidian x Mobile Theme Light Mode"/>
|
||||
</p>
|
||||
|
||||
- Desktop
|
||||
<p align="center">
|
||||
<img src="./docs/preview/preview06.png" width="700" alt="Obsidian Xscriptor Desktop Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview07.png" width="700" alt="Obsidian Xscriptor Desktop Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview06.png" width="700" alt="Obsidian x Desktop Theme Dark Mode"/>
|
||||
<img src="./docs/preview/preview07.png" width="700" alt="Obsidian x Desktop Theme Dark Mode"/>
|
||||
</p>
|
||||
|
||||
## References
|
||||
- Official theme documentation: [Build a theme](https://docs.obsidian.md/Themes/App+themes/Build+a+theme)
|
||||
- Obsidian CSS variables: [About styling](https://docs.obsidian.md/Reference/CSS+variables/About+styling)
|
||||
- Migration guide and best practices: [1.0 Theme migration guide](https://obsidian.md/blog/1-0-theme-migration-guide/)
|
||||
- Official template: [obsidian-sample-theme](https://github.com/obsidianmd/obsidian-sample-theme)
|
||||
|
||||
## License
|
||||
[MIT License](LICENSE)
|
||||
|
||||
### Credits
|
||||
Typography: EB Garamond — licensed under SIL Open Font License 1.1.
|
||||
|
||||
**Created by [Xscriptor](https://xscriptor.com)** · [Report Issues](https://github.com/xscriptor/obsidian/issues)
|
||||
## Credits
|
||||
Typography: EB Garamond, licensed under the SIL Open Font License 1.1.
|
||||
|
|
|
|||
191
docs/apply-palette.md
Normal file
191
docs/apply-palette.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
<h1 align="center">apply_palette.py</h1>
|
||||
|
||||
<p align="center">A short guide to configure and run the theme palette generator for <code>x</code>.</p>
|
||||
|
||||
<h2 align="center">Overview</h2>
|
||||
|
||||
This script reads the editable configuration from `theme-palette.json` and applies it to `theme.css`.
|
||||
|
||||
It updates:
|
||||
|
||||
- core theme colors
|
||||
- light mode variables
|
||||
- dark mode variables
|
||||
- code block colors
|
||||
- focus shadows
|
||||
- embedded font settings
|
||||
|
||||
<h2 align="center">Requirements</h2>
|
||||
|
||||
Before running the script, make sure you have:
|
||||
|
||||
- Python 3 installed
|
||||
- the repository opened in your terminal
|
||||
- `theme-palette.json` in the project root
|
||||
- `theme.css` in the project root
|
||||
|
||||
<h2 align="center">Project Structure</h2>
|
||||
|
||||
The command assumes this layout:
|
||||
|
||||
```text
|
||||
obsidian/
|
||||
├── docs/
|
||||
├── scripts/
|
||||
│ └── apply_palette.py
|
||||
├── theme-palette.json
|
||||
└── theme.css
|
||||
```
|
||||
|
||||
<h2 align="center">How To Run</h2>
|
||||
|
||||
From the project root, run:
|
||||
|
||||
```bash
|
||||
python3 scripts/apply_palette.py --config theme-palette.json --input theme.css --output theme.css
|
||||
```
|
||||
|
||||
This command:
|
||||
|
||||
- loads the configuration file
|
||||
- reads the current CSS
|
||||
- replaces the configured values
|
||||
- writes the result back into `theme.css`
|
||||
|
||||
<h2 align="center">Arguments</h2>
|
||||
|
||||
The script supports these arguments:
|
||||
|
||||
- `--config`: path to the editable JSON configuration file
|
||||
- `--input`: path to the source CSS file
|
||||
- `--output`: path to the output CSS file
|
||||
|
||||
Example with custom paths:
|
||||
|
||||
```bash
|
||||
python3 scripts/apply_palette.py \
|
||||
--config theme-palette.json \
|
||||
--input theme.css \
|
||||
--output theme.css
|
||||
```
|
||||
|
||||
<h2 align="center">Editing Colors</h2>
|
||||
|
||||
Open `theme-palette.json` and edit the palette sections in order.
|
||||
|
||||
Main sections:
|
||||
|
||||
- `palette.core_colors`: main accent and heading colors, also linked to Style Settings
|
||||
- `palette.light_mode`: variables used in light mode
|
||||
- `palette.dark_mode`: variables used in dark mode
|
||||
- `palette.code`: code block, inline code, and syntax colors
|
||||
- `palette.focus_shadows`: regex-based replacements for focus outlines
|
||||
- `palette.replacements`: direct string replacements for fixed CSS values
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "accent",
|
||||
"setting_id": "accent-color",
|
||||
"css_var": "--accent-color",
|
||||
"light": "#2563eb",
|
||||
"dark": "#7aa2ff"
|
||||
}
|
||||
```
|
||||
|
||||
<h2 align="center">Editing Fonts</h2>
|
||||
|
||||
The `font` section controls the font family and embedded font data used in `theme.css`.
|
||||
|
||||
Important fields:
|
||||
|
||||
- `family_name`: font family name written into the `@font-face` blocks
|
||||
- `text_stack`: font stack for body text
|
||||
- `interface_stack`: font stack for interface elements
|
||||
- `monospace_stack`: font stack for code and monospace use
|
||||
- `faces`: list of embedded font faces
|
||||
|
||||
Each font face supports:
|
||||
|
||||
- `label`: internal label for the face
|
||||
- `style`: usually `normal` or `italic`
|
||||
- `weight`: the CSS weight or range used by that face
|
||||
- `format`: usually `woff2`
|
||||
- `keep_existing_base64`: keeps the current base64 already stored in `theme.css`
|
||||
- `source_path`: loads a local font file and converts it to base64
|
||||
- `source_base64`: lets you paste the base64 string directly
|
||||
|
||||
<h2 align="center">Keeping The Current Embedded Font</h2>
|
||||
|
||||
If you want to preserve the current embedded font data, keep this:
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "regular",
|
||||
"style": "normal",
|
||||
"weight": "400 700",
|
||||
"format": "woff2",
|
||||
"keep_existing_base64": true,
|
||||
"source_path": "",
|
||||
"source_base64": ""
|
||||
}
|
||||
```
|
||||
|
||||
<h2 align="center">Replacing The Embedded Font From A File</h2>
|
||||
|
||||
If you want the script to embed a new font automatically, point `source_path` to a local `.woff2` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "regular",
|
||||
"style": "normal",
|
||||
"weight": "400 700",
|
||||
"format": "woff2",
|
||||
"keep_existing_base64": false,
|
||||
"source_path": "fonts/YourFont-Regular.woff2",
|
||||
"source_base64": ""
|
||||
}
|
||||
```
|
||||
|
||||
Do the same for the italic face if needed.
|
||||
|
||||
<h2 align="center">Replacing The Embedded Font With Raw Base64</h2>
|
||||
|
||||
If you already have the encoded font, paste it into `source_base64`:
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "italic",
|
||||
"style": "italic",
|
||||
"weight": "400 700",
|
||||
"format": "woff2",
|
||||
"keep_existing_base64": false,
|
||||
"source_path": "",
|
||||
"source_base64": "PASTE_YOUR_BASE64_HERE"
|
||||
}
|
||||
```
|
||||
|
||||
<h2 align="center">Recommended Workflow</h2>
|
||||
|
||||
1. Edit `theme-palette.json`.
|
||||
2. Save your changes.
|
||||
3. Run `apply_palette.py`.
|
||||
4. Reload the theme in Obsidian.
|
||||
5. Repeat until the palette and fonts look right.
|
||||
|
||||
<h2 align="center">Troubleshooting</h2>
|
||||
|
||||
If the script fails:
|
||||
|
||||
- make sure you are running it from the project root
|
||||
- confirm that `theme-palette.json` is valid JSON
|
||||
- confirm that the CSS structure still matches the theme blocks the script expects
|
||||
- verify that any `source_path` points to a real `.woff2` file
|
||||
- check that `source_base64` is a valid base64 string if you use it
|
||||
|
||||
<h2 align="center">Notes</h2>
|
||||
|
||||
- The script overwrites the output file you provide.
|
||||
- Using the same file for `--input` and `--output` is supported.
|
||||
- If you edit `theme.css` structure heavily, some automated replacements may need to be updated in the script.
|
||||
2179
example.css
Normal file
2179
example.css
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Xscriptor",
|
||||
"name": "X",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "1.0.0",
|
||||
"description": "A refined literary-inspired theme for Obsidian, featuring EB Garamond typography and flexible customization for any workspace.",
|
||||
|
|
|
|||
433
scripts/apply_palette.py
Normal file
433
scripts/apply_palette.py
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT_START = ":root {"
|
||||
ROOT_END = "\n}\n\n/* ================================================================="
|
||||
LIGHT_START = ".theme-light {"
|
||||
LIGHT_END = "\n}\n\n/* Estilos específicos para modo claro */"
|
||||
DARK_START = ".theme-dark {"
|
||||
DARK_END = "\n}\n\n/* Estilos específicos para modo oscuro */"
|
||||
|
||||
|
||||
def replace_decl(text: str, selector: str, prop: str, value: str, count: int = 1) -> str:
|
||||
pattern = re.compile(
|
||||
rf"({re.escape(selector)}\s*\{{[^}}]*?{re.escape(prop)}:\s*)[^;]+(;)",
|
||||
re.S,
|
||||
)
|
||||
new_text, replacements = pattern.subn(lambda m: f"{m.group(1)}{value}{m.group(2)}", text, count=count)
|
||||
if replacements == 0:
|
||||
raise ValueError(f"No se pudo actualizar `{prop}` en el selector `{selector}`")
|
||||
return new_text
|
||||
|
||||
|
||||
def replace_region_var(
|
||||
text: str,
|
||||
start_marker: str,
|
||||
end_marker: str,
|
||||
var_name: str,
|
||||
first_value: str,
|
||||
rest_value: str | None = None,
|
||||
) -> str:
|
||||
start = text.find(start_marker)
|
||||
if start == -1:
|
||||
raise ValueError(f"No se encontro el bloque que empieza con: {start_marker}")
|
||||
end = text.find(end_marker, start)
|
||||
if end == -1:
|
||||
raise ValueError(f"No se encontro el final del bloque: {end_marker}")
|
||||
|
||||
region = text[start:end]
|
||||
pattern = re.compile(rf"(^\s*{re.escape(var_name)}:\s*)[^;]+;", re.M)
|
||||
matches = list(pattern.finditer(region))
|
||||
if not matches:
|
||||
raise ValueError(f"No se encontro la variable `{var_name}` dentro del bloque esperado")
|
||||
|
||||
replacement_values = [first_value]
|
||||
if rest_value is None:
|
||||
replacement_values.extend([first_value] * (len(matches) - 1))
|
||||
else:
|
||||
replacement_values.extend([rest_value] * (len(matches) - 1))
|
||||
|
||||
replacement_iter = iter(replacement_values)
|
||||
region = pattern.sub(lambda m: f"{m.group(1)}{next(replacement_iter)};", region)
|
||||
return text[:start] + region + text[end:]
|
||||
|
||||
|
||||
def replace_setting_defaults(text: str, setting_id: str, light_value: str, dark_value: str) -> str:
|
||||
lines = text.splitlines(keepends=True)
|
||||
start_idx = None
|
||||
|
||||
for index, line in enumerate(lines):
|
||||
if line.strip() == f"id: {setting_id}":
|
||||
start_idx = index
|
||||
break
|
||||
|
||||
if start_idx is None:
|
||||
raise ValueError(f"No se pudieron actualizar los defaults de Style Settings para `{setting_id}`")
|
||||
|
||||
end_idx = len(lines)
|
||||
for index in range(start_idx + 1, len(lines)):
|
||||
stripped = lines[index].strip()
|
||||
if stripped == "-" or stripped == "*/":
|
||||
end_idx = index
|
||||
break
|
||||
|
||||
light_count = 0
|
||||
dark_count = 0
|
||||
for index in range(start_idx, end_idx):
|
||||
stripped = lines[index].strip()
|
||||
if stripped.startswith("default-light:"):
|
||||
indent = lines[index][: len(lines[index]) - len(lines[index].lstrip())]
|
||||
lines[index] = f"{indent}default-light: '{light_value}'\n"
|
||||
light_count += 1
|
||||
elif stripped.startswith("default-dark:"):
|
||||
indent = lines[index][: len(lines[index]) - len(lines[index].lstrip())]
|
||||
lines[index] = f"{indent}default-dark: '{dark_value}'\n"
|
||||
dark_count += 1
|
||||
|
||||
if light_count == 0 or dark_count == 0:
|
||||
raise ValueError(f"No se pudieron actualizar los defaults de Style Settings para `{setting_id}`")
|
||||
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
def list_to_value_map(items: list[dict], name_key: str = "name") -> dict[str, str]:
|
||||
return {item[name_key]: item["value"] for item in items}
|
||||
|
||||
|
||||
def apply_core_palette(css: str, core_colors: list[dict]) -> str:
|
||||
for item in core_colors:
|
||||
css_var = item["css_var"]
|
||||
light = item["light"]
|
||||
dark = item["dark"]
|
||||
|
||||
css = replace_setting_defaults(css, item["setting_id"], light, dark)
|
||||
css = replace_region_var(css, ROOT_START, ROOT_END, css_var, light)
|
||||
css = replace_region_var(css, LIGHT_START, LIGHT_END, css_var, f"var({css_var}, {light})", light)
|
||||
css = replace_region_var(css, DARK_START, DARK_END, css_var, f"var({css_var}, {dark})", dark)
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def apply_mode_palette(css: str, start_marker: str, end_marker: str, items: list[dict]) -> str:
|
||||
for item in items:
|
||||
css = replace_region_var(css, start_marker, end_marker, item["var"], item["value"])
|
||||
return css
|
||||
|
||||
|
||||
def apply_replacements(css: str, replacements: list[dict]) -> str:
|
||||
for item in replacements:
|
||||
css = css.replace(item["from"], item["to"])
|
||||
return css
|
||||
|
||||
|
||||
def update_font_face_block(css: str, font_cfg: dict, face: dict) -> str:
|
||||
pattern = re.compile(
|
||||
rf"@font-face\s*\{{(?:(?!@font-face)[\s\S])*?font-style:\s*{re.escape(face['style'])};"
|
||||
rf"(?:(?!@font-face)[\s\S])*?font-weight:\s*{re.escape(face['weight'])};"
|
||||
rf"(?:(?!@font-face)[\s\S])*?\}}",
|
||||
re.S,
|
||||
)
|
||||
|
||||
def repl(match: re.Match[str]) -> str:
|
||||
block = match.group(0)
|
||||
block = re.sub(
|
||||
r"(font-family:\s*')[^']+(';)",
|
||||
rf"\1{font_cfg['family_name']}\2",
|
||||
block,
|
||||
count=1,
|
||||
)
|
||||
|
||||
encoded = ""
|
||||
source_path = face.get("source_path", "").strip()
|
||||
source_base64 = face.get("source_base64", "").strip()
|
||||
keep_existing = face.get("keep_existing_base64", False)
|
||||
|
||||
if source_path:
|
||||
font_bytes = Path(source_path).read_bytes()
|
||||
encoded = base64.b64encode(font_bytes).decode("ascii")
|
||||
elif source_base64:
|
||||
encoded = source_base64
|
||||
elif keep_existing:
|
||||
return block
|
||||
else:
|
||||
raise ValueError(
|
||||
f"La cara de fuente `{face['label']}` necesita `source_path`, `source_base64` o `keep_existing_base64: true`"
|
||||
)
|
||||
|
||||
src_value = f'src: url("data:font/{face["format"]};base64,{encoded}");'
|
||||
block, count = re.subn(r'src:\s*url\("data:font/[^"]+"\);', src_value, block, count=1)
|
||||
if count == 0:
|
||||
raise ValueError(f"No se pudo actualizar el bloque `@font-face` para `{face['label']}`")
|
||||
return block
|
||||
|
||||
new_css, count = pattern.subn(repl, css, count=1)
|
||||
if count == 0:
|
||||
raise ValueError(f"No se encontro el bloque `@font-face` para `{face['label']}`")
|
||||
return new_css
|
||||
|
||||
|
||||
def apply_font_config(css: str, font_cfg: dict) -> str:
|
||||
for face in font_cfg.get("faces", []):
|
||||
css = update_font_face_block(css, font_cfg, face)
|
||||
|
||||
text_stack = font_cfg["text_stack"]
|
||||
interface_stack = font_cfg["interface_stack"]
|
||||
monospace_stack = font_cfg["monospace_stack"]
|
||||
|
||||
css = replace_decl(css, "body", "font-family", f"{text_stack} !important")
|
||||
css = replace_decl(css, "body", "--font-text", text_stack)
|
||||
css = replace_decl(css, "body", "--font-interface", interface_stack)
|
||||
css = replace_decl(css, "body", "--font-monospace", monospace_stack)
|
||||
|
||||
font_selectors = [
|
||||
"h1, h2, h3, h4, h5, h6",
|
||||
".markdown-source-view,\n.markdown-preview-view",
|
||||
".nav-folder-title,\n.nav-file-title",
|
||||
".workspace-tab-header-inner",
|
||||
".status-bar",
|
||||
"button",
|
||||
"input, textarea, select",
|
||||
".menu-item",
|
||||
"table",
|
||||
]
|
||||
for selector in font_selectors:
|
||||
css = replace_decl(css, selector, "font-family", f"{text_stack} !important", count=0)
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def apply_code_palette(css: str, code: dict[str, str]) -> str:
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered pre,\n.theme-dark .markdown-rendered pre code",
|
||||
"background",
|
||||
code["dark_block_background"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered pre,\n.theme-dark .markdown-rendered pre code",
|
||||
"color",
|
||||
code["dark_code_text"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered pre,\n.theme-dark .markdown-rendered pre code",
|
||||
"border",
|
||||
f"1px solid {code['dark_code_border']}",
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"background",
|
||||
code["dark_inline_background"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"color",
|
||||
code["dark_code_text"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"border",
|
||||
f"1px solid {code['dark_code_border']}",
|
||||
)
|
||||
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered pre,\n.theme-light .markdown-rendered pre code",
|
||||
"background",
|
||||
code["light_block_background"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered pre,\n.theme-light .markdown-rendered pre code",
|
||||
"color",
|
||||
code["light_code_text"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered pre,\n.theme-light .markdown-rendered pre code",
|
||||
"border",
|
||||
f"1px solid {code['light_code_border']}",
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"background",
|
||||
code["light_inline_background"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"color",
|
||||
code["light_code_text"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code:not([class*=\"language-\"])",
|
||||
"border",
|
||||
f"1px solid {code['light_code_border']}",
|
||||
)
|
||||
|
||||
css = replace_decl(css, ".theme-dark .cm-keyword", "color", code["dark_cm_keyword"])
|
||||
css = replace_decl(css, ".theme-dark .cm-string", "color", code["dark_cm_string"])
|
||||
css = replace_decl(css, ".theme-dark .cm-number", "color", code["dark_cm_number"])
|
||||
css = replace_decl(css, ".theme-dark .cm-atom,\n.theme-dark .cm-bool", "color", code["dark_cm_bool"])
|
||||
css = replace_decl(css, ".theme-dark .cm-property", "color", code["dark_cm_property"])
|
||||
css = replace_decl(css, ".theme-dark .cm-comment", "color", code["dark_cm_comment"])
|
||||
|
||||
css = replace_decl(css, ".theme-light .cm-keyword", "color", code["light_cm_keyword"])
|
||||
css = replace_decl(css, ".theme-light .cm-string", "color", code["light_cm_string"])
|
||||
css = replace_decl(css, ".theme-light .cm-number", "color", code["light_cm_number"])
|
||||
css = replace_decl(css, ".theme-light .cm-atom,\n.theme-light .cm-bool", "color", code["light_cm_bool"])
|
||||
css = replace_decl(css, ".theme-light .cm-property", "color", code["light_cm_property"])
|
||||
css = replace_decl(css, ".theme-light .cm-comment", "color", code["light_cm_comment"])
|
||||
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.keyword",
|
||||
"color",
|
||||
code["dark_cm_keyword"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.string",
|
||||
"color",
|
||||
code["dark_cm_string"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.number",
|
||||
"color",
|
||||
code["dark_cm_number"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.boolean",
|
||||
"color",
|
||||
code["dark_cm_bool"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.property",
|
||||
"color",
|
||||
code["dark_cm_property"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-dark .markdown-rendered code[class*=\"language-\"] .token.comment",
|
||||
"color",
|
||||
code["dark_cm_comment"],
|
||||
)
|
||||
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.keyword",
|
||||
"color",
|
||||
code["light_cm_keyword"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.string",
|
||||
"color",
|
||||
code["light_cm_string"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.number",
|
||||
"color",
|
||||
code["light_cm_number"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.boolean",
|
||||
"color",
|
||||
code["light_cm_bool"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.property",
|
||||
"color",
|
||||
code["light_cm_property"],
|
||||
)
|
||||
css = replace_decl(
|
||||
css,
|
||||
".theme-light .markdown-rendered code[class*=\"language-\"] .token.comment",
|
||||
"color",
|
||||
code["light_cm_comment"],
|
||||
)
|
||||
|
||||
css = replace_decl(css, "code", "background-color", code["dark_inline_background"])
|
||||
css = replace_decl(css, "code", "border-color", code["dark_code_border"])
|
||||
css = replace_decl(css, "pre", "background-color", code["dark_block_background"])
|
||||
css = replace_decl(css, "pre", "border-color", code["dark_code_border"])
|
||||
|
||||
css = re.sub(
|
||||
r"^code \{ background-color: [^;]+; border-color: [^;]+; text-shadow: none !important; \}$",
|
||||
f"code {{ background-color: {code['dark_inline_background']} !important; border-color: {code['dark_code_border']} !important; text-shadow: none !important; }}",
|
||||
css,
|
||||
flags=re.M,
|
||||
)
|
||||
css = re.sub(
|
||||
r"^pre \{ background-color: [^;]+; border-color: [^;]+; \}$",
|
||||
f"pre {{ background-color: {code['dark_block_background']} !important; border-color: {code['dark_code_border']} !important; }}",
|
||||
css,
|
||||
flags=re.M,
|
||||
)
|
||||
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-javascript", "--code-bg", code["javascript_code_bg"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-javascript", "--code-bd", code["javascript_code_border"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-python", "--code-bg", code["python_code_bg"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-python", "--code-bd", code["python_code_border"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-css", "--code-bg", code["css_code_bg"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-css", "--code-bd", code["css_code_border"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-html", "--code-bg", code["html_code_bg"])
|
||||
css = replace_decl(css, ".markdown-rendered pre code.language-html", "--code-bd", code["html_code_border"])
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def apply_focus_shadows(css: str, focus_items: list[dict]) -> str:
|
||||
for item in focus_items:
|
||||
css = re.sub(item["from"], item["to"], css)
|
||||
return css
|
||||
|
||||
|
||||
def apply_config(css: str, config: dict) -> str:
|
||||
palette = config["palette"]
|
||||
css = apply_core_palette(css, palette["core_colors"])
|
||||
css = apply_mode_palette(css, LIGHT_START, LIGHT_END, palette["light_mode"])
|
||||
css = apply_mode_palette(css, DARK_START, DARK_END, palette["dark_mode"])
|
||||
css = apply_replacements(css, palette["replacements"])
|
||||
css = apply_font_config(css, config["font"])
|
||||
css = apply_code_palette(css, list_to_value_map(palette["code"]))
|
||||
css = apply_focus_shadows(css, palette["focus_shadows"])
|
||||
return css
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Aplica una configuracion simple de paleta y fuentes a theme.css")
|
||||
parser.add_argument("--config", default="theme-palette.json", help="Ruta al JSON editable")
|
||||
parser.add_argument("--input", default="theme.css", help="Archivo CSS de entrada")
|
||||
parser.add_argument("--output", default="theme.css", help="Archivo CSS de salida")
|
||||
args = parser.parse_args()
|
||||
|
||||
config_path = Path(args.config)
|
||||
input_path = Path(args.input)
|
||||
output_path = Path(args.output)
|
||||
|
||||
config = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
css = input_path.read_text(encoding="utf-8")
|
||||
css = apply_config(css, config)
|
||||
output_path.write_text(css, encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
281
theme-palette.json
Normal file
281
theme-palette.json
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
{
|
||||
"palette": {
|
||||
"core_colors": [
|
||||
{
|
||||
"name": "accent",
|
||||
"setting_id": "accent-color",
|
||||
"css_var": "--accent-color",
|
||||
"light": "#2563eb",
|
||||
"dark": "#7aa2ff"
|
||||
},
|
||||
{
|
||||
"name": "heading_1",
|
||||
"setting_id": "h1-color",
|
||||
"css_var": "--h1-color",
|
||||
"light": "#7c3aed",
|
||||
"dark": "#a78bfa"
|
||||
},
|
||||
{
|
||||
"name": "heading_2",
|
||||
"setting_id": "h2-color",
|
||||
"css_var": "--h2-color",
|
||||
"light": "#2563eb",
|
||||
"dark": "#7aa2ff"
|
||||
},
|
||||
{
|
||||
"name": "heading_3",
|
||||
"setting_id": "h3-color",
|
||||
"css_var": "--h3-color",
|
||||
"light": "#0f766e",
|
||||
"dark": "#2dd4bf"
|
||||
},
|
||||
{
|
||||
"name": "italic",
|
||||
"setting_id": "italic-color",
|
||||
"css_var": "--italic-color",
|
||||
"light": "#c2410c",
|
||||
"dark": "#f5b971"
|
||||
}
|
||||
],
|
||||
"light_mode": [
|
||||
{ "var": "--background-opacity", "value": "1" },
|
||||
{ "var": "--background-primary-rgb", "value": "255, 255, 255" },
|
||||
{ "var": "--background-secondary-rgb", "value": "255, 255, 255" },
|
||||
{ "var": "--background-primary", "value": "#ffffff" },
|
||||
{ "var": "--background-primary-alt", "value": "#fefefe" },
|
||||
{ "var": "--background-secondary", "value": "#fdfdfd" },
|
||||
{ "var": "--background-secondary-alt", "value": "#fcfcfd" },
|
||||
{ "var": "--background-accent", "value": "#ffffff" },
|
||||
{ "var": "--background-modifier-border", "value": "#eef1f4" },
|
||||
{ "var": "--background-modifier-form-field", "value": "#ffffff" },
|
||||
{ "var": "--background-modifier-form-field-highlighted", "value": "#fdfefe" },
|
||||
{ "var": "--background-modifier-box-shadow", "value": "rgba(15, 23, 42, 0.08)" },
|
||||
{ "var": "--background-modifier-success", "value": "#ecfdf5" },
|
||||
{ "var": "--background-modifier-error", "value": "#fef2f2" },
|
||||
{ "var": "--background-modifier-error-rgb", "value": "254, 242, 242" },
|
||||
{ "var": "--background-modifier-error-hover", "value": "#fee2e2" },
|
||||
{ "var": "--background-modifier-cover", "value": "rgba(15, 23, 42, 0.5)" },
|
||||
{ "var": "--text-normal", "value": "#1f2937" },
|
||||
{ "var": "--text-muted", "value": "#475569" },
|
||||
{ "var": "--text-faint", "value": "#94a3b8" },
|
||||
{ "var": "--text-accent", "value": "#2563eb" },
|
||||
{ "var": "--text-accent-hover", "value": "#1d4ed8" },
|
||||
{ "var": "--text-on-accent", "value": "#ffffff" },
|
||||
{ "var": "--text-error", "value": "#dc2626" },
|
||||
{ "var": "--text-warning", "value": "#d97706" },
|
||||
{ "var": "--text-success", "value": "#059669" },
|
||||
{ "var": "--h4-color", "value": "#4f46e5" },
|
||||
{ "var": "--h5-color", "value": "#334155" },
|
||||
{ "var": "--h6-color", "value": "#64748b" },
|
||||
{ "var": "--border-color", "value": "#eef1f4" },
|
||||
{ "var": "--border-color-hover", "value": "#e8edf2" },
|
||||
{ "var": "--border-color-focus", "value": "#dce3ea" },
|
||||
{ "var": "--divider-color", "value": "#f0f3f6" },
|
||||
{ "var": "--divider-color-hover", "value": "#e8edf2" },
|
||||
{ "var": "--interactive-normal", "value": "#ffffff" },
|
||||
{ "var": "--interactive-hover", "value": "#fbfcfe" },
|
||||
{ "var": "--interactive-accent", "value": "#2563eb" },
|
||||
{ "var": "--interactive-accent-hover", "value": "#1d4ed8" },
|
||||
{ "var": "--interactive-success", "value": "#059669" },
|
||||
{ "var": "--callout-info", "value": "#eff6ff" },
|
||||
{ "var": "--callout-info-border", "value": "#3b82f6" },
|
||||
{ "var": "--callout-info-text", "value": "#1e3a8a" },
|
||||
{ "var": "--callout-warning", "value": "#fff7ed" },
|
||||
{ "var": "--callout-warning-border", "value": "#f59e0b" },
|
||||
{ "var": "--callout-warning-text", "value": "#9a3412" },
|
||||
{ "var": "--callout-error", "value": "#fef2f2" },
|
||||
{ "var": "--callout-error-border", "value": "#ef4444" },
|
||||
{ "var": "--callout-error-text", "value": "#991b1b" },
|
||||
{ "var": "--callout-success", "value": "#ecfdf5" },
|
||||
{ "var": "--callout-success-border", "value": "#10b981" },
|
||||
{ "var": "--callout-success-text", "value": "#065f46" },
|
||||
{ "var": "--callout-tip", "value": "#f5f3ff" },
|
||||
{ "var": "--callout-tip-border", "value": "#8b5cf6" },
|
||||
{ "var": "--callout-tip-text", "value": "#5b21b6" },
|
||||
{ "var": "--callout-note", "value": "#fcfdff" },
|
||||
{ "var": "--callout-note-border", "value": "#94a3b8" },
|
||||
{ "var": "--callout-note-text", "value": "#334155" },
|
||||
{ "var": "--tag-background", "value": "#eff6ff" },
|
||||
{ "var": "--tag-text", "value": "#1d4ed8" },
|
||||
{ "var": "--tag-border", "value": "#bfdbfe" },
|
||||
{ "var": "--highlight-yellow", "value": "#fef08a" },
|
||||
{ "var": "--highlight-green", "value": "#bbf7d0" },
|
||||
{ "var": "--highlight-blue", "value": "#bfdbfe" },
|
||||
{ "var": "--highlight-pink", "value": "#fbcfe8" },
|
||||
{ "var": "--highlight-purple", "value": "#ddd6fe" },
|
||||
{ "var": "--table-header-background", "value": "#fcfdff" },
|
||||
{ "var": "--table-row-even", "value": "#fcfdff" },
|
||||
{ "var": "--table-row-odd", "value": "#ffffff" },
|
||||
{ "var": "--table-border-color", "value": "#eef1f4" },
|
||||
{ "var": "--card-color", "value": "#ffffff" },
|
||||
{ "var": "--select-color", "value": "#ffffff" },
|
||||
{ "var": "--input-color", "value": "#fcfdff" }
|
||||
],
|
||||
"dark_mode": [
|
||||
{ "var": "--background-primary-rgb", "value": "10, 10, 10" },
|
||||
{ "var": "--background-secondary-rgb", "value": "18, 18, 18" },
|
||||
{ "var": "--background-primary", "value": "rgba(10, 10, 10, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-primary-alt", "value": "rgba(15, 15, 15, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-secondary", "value": "rgba(18, 18, 18, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-secondary-alt", "value": "rgba(21, 21, 21, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-accent", "value": "rgba(10, 10, 10, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-modifier-border", "value": "#2a2a2a" },
|
||||
{ "var": "--background-modifier-form-field", "value": "rgba(0, 0, 0, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-modifier-form-field-highlighted", "value": "rgba(0, 0, 0, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--background-modifier-box-shadow", "value": "rgba(0, 0, 0, 0.32)" },
|
||||
{ "var": "--background-modifier-success", "value": "#0f1f17" },
|
||||
{ "var": "--background-modifier-error", "value": "#2b1418" },
|
||||
{ "var": "--background-modifier-error-rgb", "value": "43, 20, 24" },
|
||||
{ "var": "--background-modifier-error-hover", "value": "#3a1b21" },
|
||||
{ "var": "--background-modifier-cover", "value": "rgba(0, 0, 0, 0.9)" },
|
||||
{ "var": "--text-normal", "value": "#e5e7eb" },
|
||||
{ "var": "--text-muted", "value": "#a1a1aa" },
|
||||
{ "var": "--text-faint", "value": "#71717a" },
|
||||
{ "var": "--text-accent", "value": "#7aa2ff" },
|
||||
{ "var": "--text-accent-hover", "value": "#a5b8ff" },
|
||||
{ "var": "--text-on-accent", "value": "#0b1020" },
|
||||
{ "var": "--text-error", "value": "#f87171" },
|
||||
{ "var": "--text-warning", "value": "#fbbf24" },
|
||||
{ "var": "--text-success", "value": "#34d399" },
|
||||
{ "var": "--h4-color", "value": "#38bdf8" },
|
||||
{ "var": "--h5-color", "value": "#f472b6" },
|
||||
{ "var": "--h6-color", "value": "#94a3b8" },
|
||||
{ "var": "--color-red-rgb", "value": "248, 113, 113" },
|
||||
{ "var": "--color-orange-rgb", "value": "245, 185, 113" },
|
||||
{ "var": "--color-yellow-rgb", "value": "251, 191, 36" },
|
||||
{ "var": "--color-green-rgb", "value": "52, 211, 153" },
|
||||
{ "var": "--color-cyan-rgb", "value": "56, 189, 248" },
|
||||
{ "var": "--color-blue-rgb", "value": "122, 162, 255" },
|
||||
{ "var": "--color-purple-rgb", "value": "167, 139, 250" },
|
||||
{ "var": "--color-pink-rgb", "value": "244, 114, 182" },
|
||||
{ "var": "--table-border-color", "value": "#2a2a2a" },
|
||||
{ "var": "--border-color", "value": "#2a2a2a" },
|
||||
{ "var": "--border-color-hover", "value": "#3a3a3a" },
|
||||
{ "var": "--border-color-focus", "value": "#4b5563" },
|
||||
{ "var": "--divider-color", "value": "#26262b" },
|
||||
{ "var": "--divider-color-hover", "value": "#3a3a3a" },
|
||||
{ "var": "--interactive-normal", "value": "rgba(18, 18, 18, 0.88)" },
|
||||
{ "var": "--interactive-hover", "value": "rgba(32, 32, 36, 0.92)" },
|
||||
{ "var": "--interactive-accent", "value": "#7aa2ff" },
|
||||
{ "var": "--interactive-accent-hover", "value": "#a5b8ff" },
|
||||
{ "var": "--interactive-success", "value": "#34d399" },
|
||||
{ "var": "--callout-info", "value": "#0f172a" },
|
||||
{ "var": "--callout-info-border", "value": "#60a5fa" },
|
||||
{ "var": "--callout-info-text", "value": "#bfdbfe" },
|
||||
{ "var": "--callout-warning", "value": "#2a1f12" },
|
||||
{ "var": "--callout-warning-border", "value": "#f59e0b" },
|
||||
{ "var": "--callout-warning-text", "value": "#fde68a" },
|
||||
{ "var": "--callout-error", "value": "#2b1418" },
|
||||
{ "var": "--callout-error-border", "value": "#f87171" },
|
||||
{ "var": "--callout-error-text", "value": "#fecaca" },
|
||||
{ "var": "--callout-success", "value": "#0f1f17" },
|
||||
{ "var": "--callout-success-border", "value": "#34d399" },
|
||||
{ "var": "--callout-success-text", "value": "#bbf7d0" },
|
||||
{ "var": "--callout-tip", "value": "#1f1733" },
|
||||
{ "var": "--callout-tip-border", "value": "#a78bfa" },
|
||||
{ "var": "--callout-tip-text", "value": "#ddd6fe" },
|
||||
{ "var": "--callout-note", "value": "#18181b" },
|
||||
{ "var": "--callout-note-border", "value": "#71717a" },
|
||||
{ "var": "--callout-note-text", "value": "#e5e7eb" },
|
||||
{ "var": "--tag-background", "value": "#111827" },
|
||||
{ "var": "--tag-text", "value": "#93c5fd" },
|
||||
{ "var": "--tag-border", "value": "#3b82f6" },
|
||||
{ "var": "--highlight-yellow", "value": "#4b3b15" },
|
||||
{ "var": "--highlight-green", "value": "#123524" },
|
||||
{ "var": "--highlight-blue", "value": "#172554" },
|
||||
{ "var": "--highlight-pink", "value": "#4a1d3f" },
|
||||
{ "var": "--highlight-purple", "value": "#312e81" },
|
||||
{ "var": "--table-header-background", "value": "#18181b" },
|
||||
{ "var": "--table-row-even", "value": "#131317" },
|
||||
{ "var": "--table-row-odd", "value": "#0a0a0a" },
|
||||
{ "var": "--card-color", "value": "rgba(0, 0, 0, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--select-color", "value": "rgba(0, 0, 0, var(--background-opacity, 0.8))" },
|
||||
{ "var": "--input-color", "value": "rgba(10, 10, 10, var(--background-opacity, 0.8))" }
|
||||
],
|
||||
"replacements": [
|
||||
{ "from": "border: 1px solid #2a2a2a;", "to": "border: 1px solid var(--border-color);" },
|
||||
{ "from": "border: 2px solid #2a2a2a;", "to": "border: 2px solid var(--border-color);" },
|
||||
{ "from": "border-color: #2a2a2a !important;", "to": "border-color: var(--border-color) !important;" },
|
||||
{ "from": "border: 1px solid #555555;", "to": "border: 1px solid var(--border-color);" },
|
||||
{ "from": "border: 2px solid #555555;", "to": "border: 2px solid var(--border-color);" },
|
||||
{ "from": "border-color: #555555 !important;", "to": "border-color: var(--border-color) !important;" },
|
||||
{ "from": "background-color: rgba(42, 42, 42, 0.8);", "to": "background-color: var(--background-modifier-cover);" },
|
||||
{ "from": "background-color: rgba(42, 42, 42, 0.9);", "to": "background-color: var(--background-modifier-cover);" },
|
||||
{ "from": "background-color: rgba(85, 85, 85, 0.8);", "to": "background-color: var(--background-modifier-cover);" }
|
||||
],
|
||||
"code": [
|
||||
{ "name": "dark_block_background", "value": "#0f1117" },
|
||||
{ "name": "dark_inline_background", "value": "#11151c" },
|
||||
{ "name": "dark_code_text", "value": "#e6edf3" },
|
||||
{ "name": "dark_code_border", "value": "#263041" },
|
||||
{ "name": "light_block_background", "value": "#f8fafc" },
|
||||
{ "name": "light_inline_background", "value": "#eef4fa" },
|
||||
{ "name": "light_code_text", "value": "#1f2937" },
|
||||
{ "name": "light_code_border", "value": "#dbe3ee" },
|
||||
{ "name": "dark_cm_keyword", "value": "#a78bfa" },
|
||||
{ "name": "dark_cm_string", "value": "#86efac" },
|
||||
{ "name": "dark_cm_number", "value": "#f5b971" },
|
||||
{ "name": "dark_cm_bool", "value": "#7dd3fc" },
|
||||
{ "name": "dark_cm_property", "value": "#93c5fd" },
|
||||
{ "name": "dark_cm_comment", "value": "#6b7280" },
|
||||
{ "name": "light_cm_keyword", "value": "#6d28d9" },
|
||||
{ "name": "light_cm_string", "value": "#047857" },
|
||||
{ "name": "light_cm_number", "value": "#c2410c" },
|
||||
{ "name": "light_cm_bool", "value": "#1d4ed8" },
|
||||
{ "name": "light_cm_property", "value": "#0f766e" },
|
||||
{ "name": "light_cm_comment", "value": "#64748b" },
|
||||
{ "name": "javascript_code_bg", "value": "#1a1d28" },
|
||||
{ "name": "javascript_code_border", "value": "#3b4261" },
|
||||
{ "name": "python_code_bg", "value": "#0f1f2a" },
|
||||
{ "name": "python_code_border", "value": "#1f4b63" },
|
||||
{ "name": "css_code_bg", "value": "#20152a" },
|
||||
{ "name": "css_code_border", "value": "#5b3b7a" },
|
||||
{ "name": "html_code_bg", "value": "#241a12" },
|
||||
{ "name": "html_code_border", "value": "#7c4f2a" }
|
||||
],
|
||||
"focus_shadows": [
|
||||
{
|
||||
"from": "box-shadow: 0 0 0 2px rgba\\(185, 138, 83, 0\\.25\\);",
|
||||
"to": "box-shadow: 0 0 0 2px rgba(122, 162, 255, 0.25);"
|
||||
},
|
||||
{
|
||||
"from": "box-shadow: 0 0 0 2px rgba\\(185, 138, 83, 0\\.18\\);",
|
||||
"to": "box-shadow: 0 0 0 2px rgba(122, 162, 255, 0.18);"
|
||||
},
|
||||
{
|
||||
"from": "box-shadow: 0 0 0 2px rgba\\(26, 115, 232, 0\\.5\\);",
|
||||
"to": "box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.22);"
|
||||
},
|
||||
{
|
||||
"from": "box-shadow: 0 0 0 2px rgba\\(26, 115, 232, 0\\.1\\);",
|
||||
"to": "box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.12);"
|
||||
}
|
||||
]
|
||||
},
|
||||
"font": {
|
||||
"family_name": "EB Garamond",
|
||||
"text_stack": "'EB Garamond', Georgia, serif",
|
||||
"interface_stack": "'EB Garamond', Georgia, serif",
|
||||
"monospace_stack": "'Courier New', monospace",
|
||||
"faces": [
|
||||
{
|
||||
"label": "regular",
|
||||
"style": "normal",
|
||||
"weight": "400 700",
|
||||
"format": "woff2",
|
||||
"keep_existing_base64": true,
|
||||
"source_path": "",
|
||||
"source_base64": ""
|
||||
},
|
||||
{
|
||||
"label": "italic",
|
||||
"style": "italic",
|
||||
"weight": "400 700",
|
||||
"format": "woff2",
|
||||
"keep_existing_base64": true,
|
||||
"source_path": "",
|
||||
"source_base64": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue