A reactive variable system, spatial overlay workspace, and structural outliner plugin for Obsidian.
Define custom text wrappers, inline color styles, CSS variables, and structural layouts directly inside your notes using a simple, readable syntax. Create floating sticky notes, copy styled content to external apps, and protect LaTeX from formatting collisions — all from a single :::vars block.
[!WARNING] This plugin is currently in Beta and there may be bugs. If you encounter any issues, please report them on the GitHub Issues page.
(text) red, "text" blue, or ^text^ into a bold header by defining a simple rule.hh text hh for highlighting. Letters must be spaced from the content to avoid false matches._&text&_).##colors and ##text to stack a color and a text style simultaneously.header_size = 24 become CSS variables (--header_size: 24px) on the document container.title, body) and link it to a wrapper with text_{name}_size.- and + list markers are intercepted and replaced with aesthetic bullet characters (•, ◦, ▸, ▹, ⁃, ·) per indent level.![[...]] or ) are skipped by the outliner.# on a color property line inside a :::vars block spawns a floating swatch palette with 9 preset colors and a custom color picker button.:::vars blocks and writes clean text to the clipboard.text/plain: Clean, variable-stripped markdown text.text/html: Compiled HTML with inline CSS (<span style="...">) so styles survive when pasted into Google Docs, Word, etc.[!WARNING] The sticky notes function is currently undergoing fixes and is temporarily disabled.
#notes scope in :::vars — Configure sticky note defaults programmatically::::vars
#notes
text_size = 14
text_colour = #333333
note_size = 120 x 80
note_colour = #fffae6
:::
:::vars block and saved with the document.data.json. Any variables defined here apply as a universal foundation across all notes in the vault.:::vars blocks automatically inherit, merge with, and override global defaults.src/templates/ directory is structured to accept community-contributed layout configurations via GitHub Pull Requests.$...$ and $$...$$) override the variable engine. Any character ranges inside math wrappers are completely masked out, preventing formatting collisions with LaTeX symbols like underscores, carets, or braces.:::vars block features an inline fold toggle. Fold it to see a summary like ▶ [VARS: 4 colors, 2 styles].:::vars blockPlace this block anywhere in your note. It defines all your styling rules.
:::vars
##colors
() = #ef4444
"" = #3b82f6
hh = #10b981
&& = #8b5cf6
##text
header_size = 32
paragraph_size = 14
^^ = header
.. = paragraph
__ = underline
&& = bold
# Or use the named syntax:
##text
text_title_size = 32
text_body_size = 14
title = ^^
body = ..
__ = underline
:::
Rules are organized under section headers prefixed with ##:
| Section | Purpose |
|---|---|
##colors (or ##colour, ##colours) |
Rules here treat values as colors. Wrapped text will be colored. |
##text |
Rules here treat values as CSS class names or text styles. Use text_name_size = number and name = ^^ for named variables. |
#notes |
Configures spatial overlay sticky note defaults (text_size, text_colour, note_size, note_colour). |
##colors)Define a wrapper symbol and assign it a color value.
##colors
() = #ef4444
"" = #3b82f6
Then use them in your note:
(This text will be red!)
"This text will be blue!"
Result: The wrapper symbols are hidden. You only see the styled text.
Simplified Color Names & Letter Codes:
Rather than using hex codes, you can also use standard color names or simple letter abbreviations directly in your variables (e.g. () = pr, -- = red, bl = pink):
hp / black / pure black → #000000wt / white → #FFFFFFrd / red → #FF0000gn / green → #00FF00bl / blue → #0000FFyl / yellow → #FFFF00mg / magenta → #FF00FFcy / cyan → #00FFFFor / orange → #FFA500pl / purple → #800080pr / pink → #FFC0CBtl / teal → #008080br / brown → #A52A2AThese will automatically resolve to their respective HEX color values under the hood.
##text)Define wrappers that apply CSS classes instead of colors.
##text
^^ = header
__ = underline
Usage:
^This becomes a header^
_This becomes underlined_
Named text variables:
Name text styles anything you want using the text_{name}_size = {number} convention:
##text
text_title_size = 32
text_body_size = 14
title = ^^
body = ..
Now ^Title text^ uses --text_title_size: 32px and .Body text. uses --text_body_size: 14px.
Built-in styles:
| Class | Effect |
|---|---|
header |
Bold text, sized by header_size or text_header_size (default 1.5em) |
paragraph |
Normal text, sized by paragraph_size or text_paragraph_size (default 1em) |
bold |
Bold text |
italic |
Italic text |
underline |
Underlined text |
strikethrough |
|
highlight |
Applies a background highlight color |
(You can define any other value and style it yourself with a CSS snippet targeting .rv-{value})
Nesting wrappers:
_&This text is bold and underlined!&_
Combining styles: Define the same wrapper in both sections:
:::vars
##colors
&& = #ff0000
##text
&& = header
header_size = 70
:::
Now, writing &Huge red header!& applies the color and the header size simultaneously.
Use letter pairs as wrappers. They must be spaced from the content:
##colors
hh = #10b981
Usage:
hh This text will be green hh
Why spaces? To prevent false matches with normal words that happen to start and end with the same letters.
Alphanumeric keys with underscores or hyphens become CSS variables:
header_size = 24
paragraph_size = 14
These become --header_size: 24px and --paragraph_size: 14px on the document container. The built-in .rv-header and .rv-paragraph classes reference these variables.
Named convention:
text_title_size = 32
text_body_size = 14
These become --text_title_size: 32px and --text_body_size: 14px. Named text variables (title = ^^) automatically pick up the matching size variable.
| Key | Type | Start | End | Example |
|---|---|---|---|---|
() |
Asymmetric symbols | ( |
) |
(colored text) |
"" |
Symmetric symbols | " |
" |
"colored text" |
^^ |
Symmetric symbols | ^ |
^ |
^header text^ |
hh |
Letter wrapper | hh |
hh |
hh highlighted hh |
Asymmetric (2 different chars): first char = start, second char = end. Symmetric (2 same chars): that char = both start and end. Letters (2+ letters): the full key is used, must be surrounded by spaces.
In the editor, every hex color value in your :::vars block gets a small color swatch next to it. Click the swatch to open your system's native color picker — changing the color automatically updates the hex code in your note.
When typing inside a :::vars block on a color property line, type # to trigger a floating color palette popup. Select from 9 preset swatches or click "🌈 Custom color" for the full system picker. The hex value is written directly into the text.
Right-click in the editor and select Create sticky note to spawn a floating note. Notes support:
Configure defaults with a #notes section in your :::vars block.
Open Settings → Concrete and use the Global layout preset dropdown or the Global configuration defaults text area to define variables that apply to every note. These act as a universal base — local :::vars blocks in each note inherit and can override them.
Click the layout icon in the ribbon or use the command Insert layout preset to open the preset selector. Choose from presets like Classic Red & Blue, Minimalist Mint, Cozy Journal, Academic & Research, or Spatial Brainstorming & Mindmap to instantly configure variables and note styling.
The selected scheme's :::vars block is inserted at the top of your note.
The plugin settings panel is organized into four categories:
200x150 or 160).#fffbeb).#451a03).14px).main.js, manifest.json, and styles.css from the latest release.VaultFolder/.obsidian/plugins/concrete-extension/..obsidian/plugins/ folder.npm installnpm run dev — compiles and watches for changes.npm run build — production build.npm run lint — checks for style errors.