Inline & Block Value Replacement
Write real, valid values in your template files, such as an actual hex color, an actual unit or actual sample data, and let a CodeBaker: replace comment swap them for placeholders at generation time. The value stays valid for your IDE while you edit the template.
The Problem This Solves
Here is the whole idea in one example. The template keeps real colors and sizes, and two directives describe what to swap:
https://codebaker.dev/examples/replace-theme.zipNeeds the CodeBaker plugin and an open project. Another IDE, or the button does nothing? Copy the link, then in the IDE right-click any folder → New → Generate from Template → Install Template from URL… and paste it. You always get a confirmation dialog before anything is installed.
{
// The folder name when the template is installed under .CodeBaker/
id: "replace-theme",
name: "Theme",
vars: {
primary: "#7c3aed",
radius: "4px",
},
}
/* CodeBaker: replace 8px => {{ radius }} */
.button {
background: #007bff; /* CodeBaker: replace #007bff => {{ primary }} */
border-radius: 8px;
}
.badge {
border-radius: 8px;
}
- primary
- #7c3aed
- radius
- 4px
.button {
background: #7c3aed;
border-radius: 4px;
}
.badge {
border-radius: 4px;
}
The IDE sees valid CSS. At generation time the replace on its own line swaps 8px everywhere below it, and the one at the end of the line swaps #007bff on that line only.
A placeholder directly inside a value works for identifiers (___ClassName___), but not for values the IDE validates:
.card { background: ___color___;}One way around it is to hide the whole declaration in a comment (/* CodeBaker: background: ___color___; */, see Keeping the Template Valid for Your IDE), but then the IDE no longer sees the declaration at all. A replace directive keeps the real value in place and describes the swap in a comment next to it:
.card {
background: #007bff; /* CodeBaker: replace #007bff => {{ COLOR }} */
}
The IDE sees a valid background: #007bff;. At generation time CodeBaker applies the swap, removes the comment and passes background: {{ COLOR }}; to Pebble like any other placeholder.
// is a comment in SCSS, LESS, JavaScript and many other languages, but not in plain CSS or a <style> block. In CSS write /* CodeBaker: ... */. The directive must be the last thing on its line (a trailing */ is removed together with the comment).
Two Modes
| Mode | Declared | Scope |
|---|---|---|
| Inline replace | Trailing comment on the same line | That one line |
| Block replace | Comment on its own line | From that line down, until paused with off or cleared with reset |
Use inline for a one-off value and block for a token repeated across many lines. If you need {% if %} or {% for %} around code, use a tag in a comment instead.
Inline Replace
<your real code> //CodeBaker: replace <value> => <replacement>
<value>is the text to find on that line. Everything before=>is joined with single spaces, soreplace solid 1px red => dashedneeds no quotes.<replacement>is everything after=>:{{ VAR }},___VAR___, a Pebble expression, or plain text.- The swap applies to that line only.
.badge {
color: #f0f0f0; //CodeBaker: replace #f0f0f0 => {{ COLOR }}
border: solid 1px red; //CodeBaker: replace solid 1px red => {{ BORDER }}
}
With COLOR = "#1a1a1a" and BORDER = "dashed 2px #999":
.badge {
color: #1a1a1a;
border: dashed 2px #999;
}
Quotes are only needed to keep runs of spaces (replace "a b" => c) or to match a value that itself contains => (replace "x => y" => z). Inside quotes, \" gives a literal quote character.
Where the replacement goes
The right-hand side lives inside a comment, so it never has to satisfy the IDE's syntax checker. The result is ordinary Pebble, so filters work: {{ color | upper }}. The only restriction is that the replacement can't contain quote characters (see Pitfalls).
Block Replace
Repeating a trailing comment on every line that uses the same token gets noisy. A block rule is declared once, on its own line, and applies to every match below it:
//CodeBaker: replace <value> => <replacement> #tag
The #tag is optional. Rules that share a tag can be paused and resumed together.
//CodeBaker: replace 1px => {{ SIZE }} #size
//CodeBaker: replace red => {{ COLOR }} #size
border: solid 1px red;
becomes
border: solid {{ SIZE }} {{ COLOR }};
A hex color such as #f0f0f0 directly after => is treated as the replacement, not as a tag. Only # tokens after the replacement are tags.
Pausing and resuming: off / on
//CodeBaker: off <value|#tag> ...
//CodeBaker: on <value|#tag> ...
off and on take one or more selectors, each a literal value or a #tag. A rule is paused or resumed if it matches at least one selector: its value equals a value selector, or its tag equals a tag selector.
off/on match by value OR tag, not bothoff 1px #size pauses every rule whose value is 1px or whose tag is size. To pause a single rule inside a shared group, give it a tag of its own.
Clearing everything: reset
//CodeBaker: reset removes every rule declared so far. Removed rules are gone for good: a later on <tag> cannot bring them back.
A rule works from its line downwards
A block rule applies to the lines below its declaration, until it is paused with off or cleared with reset. The lines above it are never changed, and each rule is applied once, so there is nothing to close at the end of the file.
a: 1px
//CodeBaker: replace 1px => {{ SIZE }}
b: 1px
becomes
a: 1px
b: {{ SIZE }}
Keeping one block as it is
One legacy block must keep its literal brand color, whatever theme is chosen. The rules for #palette are paused around it and resumed after it:
https://codebaker.dev/examples/replace-off-on.zipNeeds the CodeBaker plugin and an open project. Another IDE, or the button does nothing? Copy the link, then in the IDE right-click any folder → New → Generate from Template → Install Template from URL… and paste it. You always get a confirmation dialog before anything is installed.
{
// The folder name when the template is installed under .CodeBaker/
id: "replace-off-on",
name: "Theme with a fixed logo",
vars: {
primary: "#7c3aed",
danger: "#e11d48",
radius: "4px",
},
}
//CodeBaker: replace #2f54eb => {{ primary }} #palette
//CodeBaker: replace #ff4d4f => {{ danger }} #palette
//CodeBaker: replace 8px => {{ radius }} #layout
.button {
background: #2f54eb;
border-radius: 8px;
}
.alert {
background: #ff4d4f;
border-radius: 8px;
}
//CodeBaker: off #palette
.brand-logo-fallback {
// Always the real brand blue, never themeable
background: #2f54eb;
border-radius: 8px;
}
//CodeBaker: on #palette
.badge {
background: #ff4d4f;
border-radius: 8px;
}
- primary
- #7c3aed
- danger
- #e11d48
- radius
- 4px
.button {
background: #7c3aed;
border-radius: 4px;
}
.alert {
background: #e11d48;
border-radius: 4px;
}
.brand-logo-fallback {
// Always the real brand blue, never themeable
background: #2f54eb;
border-radius: 4px;
}
.badge {
background: #e11d48;
border-radius: 4px;
}
.button, .alert and .badge are themed. .brand-logo-fallback keeps #2f54eb because #palette was paused there, but its border-radius is still themed because #layout was never paused. Nothing has to close the rules at the end of the file.
Other Forms
- Regex:
//CodeBaker: regex <pattern> => <replacement> #taguses a Java regular expression, and the replacement can use$1-style group references. It follows the sameoff/on/resetrules.//CodeBaker: regex \d+px => {{ SIZE }} #sizes - Two-token shorthand: without
=>,replace old newtakes the first two tokens as the value and the replacement. - A
replacedirective CodeBaker can't parse (for examplereplace xwith no replacement) is not consumed. It is not an error: the text of the comment ends up in the generated file.
Pitfalls
Matching is plain substring replacement. replace red => X also changes colored, and replace 1px => X also changes 11px. Choose values that are unique in the file, include surrounding characters (": red"), or use a regex rule with word boundaries (\bred\b).
Rules chain in declaration order. Each rule sees the output of the rules before it. After replace 1px => 2px and replace 2px => 3px, 1px ends up as 3px.
Don't put quote characters in the replacement. Directive arguments go through a tokenizer that consumes " and '. {{ x | default('n/a') }} is turned into {{ x | default(n/a ) }} and fails in Pebble, and target: "{{ m.key }}" loses its quotes. Runs of unquoted spaces also collapse into one. Compute such values in a config variable and use {{ name }} in the replacement.
Rules skip raw blocks and printed strings. Text inside {% raw %} ... {% endraw %} (or {% verbatim %}), and a printed string such as {{ 'text' }}, is never rewritten by replace or regex, on one line or across many lines. A CodeBaker: line inside a raw block is only text, so it declares nothing. Rules apply again below the block.
Scope is one file. Rules never carry over from one template file to the next.
Directive words are reserved. A comment whose first word is replace, regex, on, off or reset is read as a CodeBaker command, not as Pebble.
Next Steps
- Keeping the Template Valid for Your IDE: tags in comments, for conditionals and loops
- Escaping Text: keep text that contains
{{ }}as it is - Configuration Reference: the config-level
replaceoption, for template-wide substitutions - Advanced Templates: loops, write-once files and in-place updates