Text and headings
From schema to text editing, typography and persistence.
What you are building
An editable heading and description block. Customers can change its text, color, alignment and heading size. The complete blocks/text.html file appears below and in the example package. Copy it into your package, allow text in the parent, then rebuild the catalog.
1. Declare the content fields
Use a text setting for the heading and textarea for the description. id is the storage key, label is the editor label and default is the actual initial value. A placeholder is not a default value.
[
{"type":"text","id":"title","label":"Heading","default":"Meet us"},
{"type":"textarea","id":"text","label":"Description","default":"Tell your story."}
]Recognized IDs such as title, text and label integrate with the shared text editor. Other IDs remain editable through schema controls; do not assume every arbitrary ID supports inline editing.
2. Bind content to markup
<h2 data-text-field="[[ block.settings_path ]].title" gs-text="block.settings.title"></h2>
<p data-text-field="[[ block.settings_path ]].text" gs-text="block.settings.text"></p>The expression inside the tag displays the value. data-text-field tells the editor where to save it. Both must point to the same setting. Sections use section.settings and section.settings_path; blocks use block. Never hardcode instance IDs such as first.
Choose heading levels by document structure: the page title is an h1, sections use h2, subsections use h3. Adjust visual size through CSS or a setting rather than choosing a heading level for its appearance.
3. Add appearance controls
Use color for text color, range with unit: "px" for size and select for alignment. The example has a 20–64 px range with a 32 px default. Store a number, then append px when rendering.
<h2 style="font-size:[[ block.settings.heading_size ]]px;color:[[ block.settings.color ]]" data-text-field="[[ block.settings_path ]].title" gs-text="block.settings.title"></h2>The multiline description uses .launch-copy-body { white-space: pre-line; } to preserve ordinary line breaks. Rich text editing stores a separate formatting document in content.canvas.fields; do not manually put HTML in the plain text value.
4. Make the block available
The parent needs "blocks":[{"type":"text"}] or that entry alongside its other allowed types. The block needs a preset and the parent needs <template gs-slot="blocks"></template>. After importing, add it in the builder. A file alone does not place it in the picker.
Static and dynamic text
Put fixed interface strings in locale dictionaries and render them with t. Declare customer content as settings. Read site.name directly or connect a text setting to a dynamic source.
An empty string is a valid saved value. Avoid adding a default filter that restores text the customer intentionally removed. Plain text containing markup is escaped.
Code blocks and inline code
A theme must style rich-text code as well as headings and paragraphs. The renderer uses .rich-text pre for a code block and .rich-text code for code, including inline fragments. Add these rules to an assets/*.css file in the theme package. Use a monospace font, preserve line breaks, and scroll long lines inside the code block instead of widening the page.
This example uses GrowSite Skeleton's .sk-page wrapper and --sk-* color variables. Replace them with your own theme wrapper and tokens when reusing it:
.sk-page .rich-text pre {
max-width: 100%;
overflow-x: auto;
padding: 24px;
border: 1px solid var(--sk-border);
border-top: 3px solid var(--sk-accent);
border-radius: 16px;
background: var(--sk-surface);
color: var(--sk-text);
font: 14px/1.75 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
white-space: pre;
overflow-wrap: normal;
}
.sk-page .rich-text code {
padding: 2px 6px;
border-radius: 6px;
background: var(--sk-surface);
color: var(--sk-accent);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.sk-page .rich-text pre code {
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
color: inherit;
font: inherit;
}GrowSite Skeleton ships this styling in assets/blog.css, alongside its Blog and Comments styles. Its selectors also cover rich text outside the blog when rendered inside .sk-page. The complete implementation is in the Skeleton download.
Test inline code, multiline blocks, long lines and mobile widths. Ensure code nested inside pre does not receive a second background or padding. These rules style code presentation; they do not add syntax highlighting or execute code.
Verify the finished element
Click the preview text, edit and save it. Then edit through schema to check stale rich text does not hide the new value. Test empty text, accented characters, a long word, multiple paragraphs, both size limits and mobile. Reload the builder and compare public rendering.
Complete block file
blocks/text.html
<div class="launch-copy" style="color:[[ block.settings.color ]];text-align:[[ block.settings.alignment ]]">
<h2 data-text-field="[[ block.settings_path ]].title" style="font-size:[[ block.settings.heading_size ]]px" gs-text="block.settings.title"></h2>
<p class="launch-copy-body" data-text-field="[[ block.settings_path ]].text" gs-text="block.settings.text"></p>
</div>