softr-vibe-coding 2.1.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +33 -19
- package/SKILL.md +62 -19
- package/datasources/rest-api.md +1 -1
- package/datasources/softr-database.md +2 -0
- package/datasources/writing.md +2 -1
- package/package.json +1 -1
- package/references/airtable-automations.md +3 -2
- package/references/anti-patterns.md +7 -1
- package/references/common-patterns.md +132 -1
- package/references/editable-settings.md +238 -0
- package/references/native-chrome-styling.md +6 -2
- package/references/quick-reference.md +19 -8
- package/references/softr-mcp.md +96 -9
- package/references/static-blocks.md +97 -0
- package/ui-ux-guidelines.md +18 -2
package/references/softr-mcp.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Softr MCP Server
|
|
2
2
|
|
|
3
|
-
The official Softr MCP server (`https://mcp.softr.io/mcp`) gives an AI assistant (Claude Code, Claude Desktop, claude.ai, Cursor, ChatGPT, Mistral) direct access to a Softr **workspace**: databases, applications, vibe coding blocks, integrations (external data sources), and workflows.
|
|
3
|
+
The official Softr MCP server (`https://mcp.softr.io/mcp`) gives an AI assistant (Claude Code, Claude Desktop, claude.ai, Cursor, ChatGPT, Mistral) direct access to a Softr **workspace**: databases, applications, vibe coding blocks, integrations (external data sources), and workflows. On this workspace server, everything the assistant does happens as the connected user, with their permissions, and shows up in Studio like any other change.
|
|
4
|
+
|
|
5
|
+
**There are now TWO server classes.** Besides the workspace server above, Softr ships **per-application MCP servers** — one server per published app, exposing that app's data (and only what the app's pages actually use) through the app's own permission model. Different tools, different schema doc, and (apparently) a different identity model. See [Per-application MCP servers](#per-application-mcp-servers). (Live-enumerated 2026-08-31; roster facts below marked "roster-verified" mean the tool exists — its behavior was not necessarily exercised.)
|
|
4
6
|
|
|
5
7
|
**This file is a sibling concern to the [../datasources/](../datasources/) guides, which cover in-block data fetching (`useRecords` + `q.select()`).** The MCP runs at chat-build time, not inside the block. For Vibe Coding work it matters twice: it answers "what fields does this table have?" without any paste-ins, and it can create and deploy the block itself — no copy-paste into Studio.
|
|
6
8
|
|
|
@@ -12,9 +14,13 @@ The official Softr MCP server (`https://mcp.softr.io/mcp`) gives an AI assistant
|
|
|
12
14
|
- [Connection and auth](#connection-and-auth)
|
|
13
15
|
- [Permissions model](#permissions-model)
|
|
14
16
|
- [Vibe coding block tools](#vibe-coding-block-tools)
|
|
17
|
+
- [Adopting Studio-AI-generated code](#adopting-studio-ai-generated-code)
|
|
15
18
|
- [Vibe coding gotchas (official)](#vibe-coding-gotchas-official)
|
|
19
|
+
- [Application management tools](#application-management-tools)
|
|
16
20
|
- [Browsing integrations (external data sources)](#browsing-integrations-external-data-sources)
|
|
17
21
|
- [Softr Database tools](#softr-database-tools)
|
|
22
|
+
- [Workflows](#workflows)
|
|
23
|
+
- [Per-application MCP servers](#per-application-mcp-servers)
|
|
18
24
|
- [Two delivery paths for this skill](#two-delivery-paths-for-this-skill)
|
|
19
25
|
- [When the MCP is not installed](#when-the-mcp-is-not-installed)
|
|
20
26
|
|
|
@@ -22,11 +28,11 @@ The official Softr MCP server (`https://mcp.softr.io/mcp`) gives an AI assistant
|
|
|
22
28
|
|
|
23
29
|
| Area | What the assistant can do | Official docs |
|
|
24
30
|
|---|---|---|
|
|
25
|
-
| Databases | Query, filter, aggregate; create/update records; build tables
|
|
26
|
-
| Applications |
|
|
31
|
+
| Databases | Query, filter, aggregate; create/update **and delete** records; build **and delete** tables, fields, databases | https://docs.softr.io/mcp/databases |
|
|
32
|
+
| Applications | **Create whole apps**; manage app users and login settings; swap an app's data source; read apps, pages, blocks, permissions, user groups; preview; publish — see [Application management tools](#application-management-tools) | https://docs.softr.io/mcp/apps |
|
|
27
33
|
| Vibe coding blocks | Create and edit blocks, manage settings, visibility, versions, data source connections | https://docs.softr.io/mcp/vibe-coding |
|
|
28
34
|
| Integrations | Browse external data sources connected to the workspace, down to field level | https://docs.softr.io/mcp/integrations |
|
|
29
|
-
| Workflows | Build, test, and publish workflows | https://docs.softr.io/mcp/workflows |
|
|
35
|
+
| Workflows | Build, wire, test, and publish workflows — 26 tools and a 418-node trigger/action catalog; see [Workflows](#workflows) | https://docs.softr.io/mcp/workflows |
|
|
30
36
|
|
|
31
37
|
`list_workspaces` is often the first call — it turns "my Sales workspace" into the workspace ID every other tool needs.
|
|
32
38
|
|
|
@@ -73,9 +79,22 @@ Before writing any block code through the MCP, call `get_vibe_coding_docs` — i
|
|
|
73
79
|
| Settings / visibility | `update_vibe_coding_block_settings`, `set_vibe_coding_block_visibility`, `set_vibe_coding_block_action_visibility` |
|
|
74
80
|
| Versions | `list_vibe_coding_block_versions`, `restore_vibe_coding_block_version`, `duplicate_vibe_coding_block_from_version` |
|
|
75
81
|
| Data sources | `connect_vibe_coding_block_data_source`, `disconnect_vibe_coding_block_data_source`, `set_vibe_coding_block_data_source_sort`, `set_vibe_coding_block_data_source_record_filters` |
|
|
82
|
+
| Any block | `get_block` (roster-verified 2026-08-31; presumed to read any block type, not just vibe blocks — unconfirmed by a live call on a native block) |
|
|
76
83
|
|
|
77
84
|
Editable settings via MCP are the same fields as the block's **Content → Settings** panel; sort and record filters are the same as the **Source** tab. Duplicating from a version is the safe way to try an alternative — the original keeps working while you experiment on the copy.
|
|
78
85
|
|
|
86
|
+
## Adopting Studio-AI-generated code
|
|
87
|
+
|
|
88
|
+
When you pull a Studio-AI-generated block via `get_vibe_coding_block_code` to adopt into a project repo as source of truth: its output renders fine but ships with predictable defects. **Functional patterns in Studio output are platform-support evidence** (it surfaces undocumented capabilities before the docs do — see SKILL.md's "Platform truth sources"); **its code hygiene is not a pattern to imitate.** Cleanup pass before committing:
|
|
89
|
+
|
|
90
|
+
- **Run a formatter** — Studio output ships inconsistent indentation (observed: statements at column 0 inside a 4-space-indented component).
|
|
91
|
+
- **Hoist and consolidate brand hexes** into module-scope constants; flag near-duplicate hexes as probable unintended drift (observed: `#AE5E3D` vs `#B4603D` for one terracotta in a single block).
|
|
92
|
+
- **Fix React keys on settings-array loops** — Studio emits `key={item.label}`; use `key={index}` (see [anti-patterns.md](anti-patterns.md#editable-settings)).
|
|
93
|
+
- **Add the guards Studio omits** — conditional render for empty media settings, `whitespace-pre-line` on long-text settings, mobile nav for block-owned headers, `aria-hidden` on decorative glyphs.
|
|
94
|
+
- **Rewrite absolute self-domain URLs relative** (`https://<app>.softr.app/#x` → `/#x`) — observed as a configured setting value on a Studio-generated hero, 2026-08-31.
|
|
95
|
+
|
|
96
|
+
This is distinct from SKILL.md's no-churn rule: cleaning up a block you're ADOPTING into the repo is required; modernizing a deployed working block's syntax is still churn — don't do that.
|
|
97
|
+
|
|
79
98
|
## Vibe coding gotchas (official)
|
|
80
99
|
|
|
81
100
|
From the official MCP docs — these hold for MCP-driven and Studio-driven edits alike:
|
|
@@ -86,6 +105,24 @@ From the official MCP docs — these hold for MCP-driven and Studio-driven edits
|
|
|
86
105
|
- **Changing the code resets action permissions.** Any code change rebuilds the block's record actions at default visibility — restrictions to user groups must be re-applied. (This is Hard Constraint 21 in SKILL.md, now officially documented: tighten Action permissions only after the LAST redeploy.)
|
|
87
106
|
- **A block with an unconnected data source saves without complaint**, then errors when the page loads. If a freshly created block looks broken but the code seems right, check its data source connection first.
|
|
88
107
|
|
|
108
|
+
## Application management tools
|
|
109
|
+
|
|
110
|
+
The Applications area goes well beyond reads (all roster-verified 2026-08-31; behavior not individually exercised):
|
|
111
|
+
|
|
112
|
+
| Group | Tools |
|
|
113
|
+
|---|---|
|
|
114
|
+
| Apps | `list_applications`, `get_application`, `create_application` (create a whole app via MCP), `update_application_data_source` (point/swap the app's data source), `set_application_login` |
|
|
115
|
+
| App users | `add_application_user`, `remove_application_user`, `list_user_groups` |
|
|
116
|
+
| Pages / blocks / permissions | `list_pages`, `get_page`, `get_page_permissions`, `get_access_control`, `get_block` |
|
|
117
|
+
| Publish / preview | `preview_app`, `publish_app` |
|
|
118
|
+
| Workspace | `list_workspaces`, `get_workspace_integrations` (distinct from the [integrations drill-down](#browsing-integrations-external-data-sources) below) |
|
|
119
|
+
|
|
120
|
+
Combined with the database tools (`create_database` / `create_table` / `create_field`) and `create_vibe_coding_block` + `publish_app`, the tool set for scaffolding a full app end to end now exists. (Existence-verified only — that pipeline hasn't been run live; treat the first full scaffold as an experiment, not a routine.)
|
|
121
|
+
|
|
122
|
+
**Etiquette from the server's own instructions:** after changing a block, link the page as `https://studio.softr.io/applications/{applicationId}/pages/{pageId}`; offer `preview_app` or `publish_app`, but **only publish when the user asks**.
|
|
123
|
+
|
|
124
|
+
> **preview_app links are auth tokens.** Per the server's own instructions, a preview link **signs its opener in as the user who requested it** and lasts about a day. Give it only to that user, and mint a fresh one with another `preview_app` call rather than re-sending an old link. Never paste a preview link into a shared channel.
|
|
125
|
+
|
|
89
126
|
## Browsing integrations (external data sources)
|
|
90
127
|
|
|
91
128
|
An integration is an external data source connected once per workspace (the builder says "integrations", the tools say "data sources" — same thing). Five read-only tools drill down from workspace to fields; each level needs an ID from the level above:
|
|
@@ -111,19 +148,25 @@ Getting this wrong **fails silently** — the code compiles, saves, and looks ri
|
|
|
111
148
|
|
|
112
149
|
## Softr Database tools
|
|
113
150
|
|
|
114
|
-
For Softr's native databases the MCP goes far beyond browsing: `get_schema` (authoritative field-type + filter-operator reference — call it before
|
|
151
|
+
For Softr's native databases the MCP goes far beyond browsing: `get_schema` (authoritative field-type + filter-operator reference — call it before creating/updating tables, fields, or filters; the server's own instructions say "do not guess field types, options, or operators"), database/table/field CRUD **including deletes** (`delete_database`, `delete_table`, `delete_field`), `list_views`, record reads (`list_records`, `search_records`, `get_record`), record writes (`create_record`, `create_records` batch, `update_record`, `delete_record`, `delete_records` batch), and `aggregate_data` for grouped summaries.
|
|
152
|
+
|
|
153
|
+
**Call economy (from the server's own instructions):** `get_table` returns a table's metadata AND all its field definitions in one call; `list_fields` returns the fields alone. Call ONE of them once per table and reuse the result — never both. (Sensible extension: re-fetch only after you changed the table's fields yourself.)
|
|
154
|
+
|
|
155
|
+
**get_schema, live-confirmed 2026-08-31:** `readOnlyFieldTypes` = AUTONUMBER, COUNT, CREATED_AT, CREATED_BY, FORMULA, LOOKUP, RECORD_ID, ROLLUP, UPDATED_AT, UPDATED_BY (matches this file's long-standing claim verbatim). The `LINKED_RECORD` value example is `["record-id-1", "record-id-2"]` — independently corroborating the verified string-array write shape in [../datasources/softr-database.md](../datasources/softr-database.md). Operator families include relative-date `IS_WITHIN` / `IS_NOT_WITHIN` ("last 7 days"), ternary `IS_BETWEEN` / `IS_NOT_BETWEEN`, and `AND`/`OR` composites. **Schema-drift caution:** the workspace server's `get_schema` and the [per-application servers'](#per-application-mcp-servers) `get_schema` have drifted — the per-app catalog lists creatable types the workspace one omits (ADDRESS, PROGRESS, TIME, DATE_RANGE, BUTTON), and even the operator NAMES differ between server kinds (workspace `GREATER_THAN` / `DOES_NOT_CONTAIN` vs per-app `GT` / `DOES_NOT_CONTAINS`) — so filter payloads are not portable between them. Always call `get_schema` on the server you are actually using.
|
|
115
156
|
|
|
116
157
|
Known limits and behaviors (per official docs):
|
|
117
158
|
|
|
118
159
|
- Record field keys are **field IDs**, not labels — `list_fields` maps between them.
|
|
119
160
|
- Computed fields (formula, lookup, rollup, count) and system fields (created/updated time and by, autonumber, record ID) are read-only; a field's type cannot be changed after creation.
|
|
120
|
-
- **
|
|
161
|
+
- **Deletion now exists** (supersedes the earlier "nothing can be deleted through the MCP yet" finding): `delete_record`, `delete_records` (batch), `delete_field`, `delete_table`, and `delete_database` are all in the roster (verified 2026-08-31), and per-app servers add `delete_record` + `batch_delete_records`. Roster-verified only — no destructive call was made, so which Databases permission level gates them and how cascades behave (e.g. deleting a table with linked records) are untested. Treat every delete as irreversible; no soft-delete is documented.
|
|
121
162
|
- **Attachment writes take a URL and copy the file.** `create_record` / `update_record` accept
|
|
122
163
|
`{ filename, url }` on an ATTACHMENT field with any publicly reachable URL; Softr fetches it, stores its
|
|
123
164
|
own copy and generates thumbnails, so backfilling images from another system is one write per record
|
|
124
165
|
with no upload step. Verified 2026-08-26 — see [../datasources/writing.md](../datasources/writing.md#attachment).
|
|
125
166
|
- **`update_field` silently ignores `allowMultipleEntries` nested inside `options`** — it is a TOP-LEVEL
|
|
126
|
-
field property; the call succeeds and changes nothing (verified 2026-08-26
|
|
167
|
+
field property; the call succeeds and changes nothing (verified 2026-08-26; the server has grown
|
|
168
|
+
substantially since — deletes added by 2026-08-31 — so this write surface is worth a re-test when
|
|
169
|
+
next touched live). To flip a LINKED_RECORD
|
|
127
170
|
field between single and multi, `PUT` it via the Tables API with `allowMultipleEntries` at top level —
|
|
128
171
|
and always echo `options.inverseLinkFieldId` in that PUT, because omitting it severs the inverse
|
|
129
172
|
pairing. Full write-up, including the silent on-write clobbering of single-valued link pairs and the
|
|
@@ -133,12 +176,56 @@ Known limits and behaviors (per official docs):
|
|
|
133
176
|
|
|
134
177
|
Typical Vibe Coding uses: "list every field on `Wigs` with id, name, type, and dropdown options", "what's the option id for `Payment status` = 'Partially paid'?", "show 3 sample records so we know value shapes", "verify the field id in my `q.select()` exists". This eliminates the field-id-typo / wrong-option-uuid class of bugs entirely.
|
|
135
178
|
|
|
179
|
+
## Workflows
|
|
180
|
+
|
|
181
|
+
Softr Workflows are automations built from trigger + action nodes, and the MCP can build, wire, test, and publish them — a **26-tool suite** (roster-verified 2026-08-31; `list_node_types` called live, the build/publish loop itself not yet exercised end to end):
|
|
182
|
+
|
|
183
|
+
| Group | Tools |
|
|
184
|
+
|---|---|
|
|
185
|
+
| Workflow lifecycle | `create_workflow`, `get_workflow`, `get_workflow_url`, `list_workflows`, `rename_workflow`, `update_workflow_configuration`, `publish_workflow`, `unpublish_workflow` |
|
|
186
|
+
| Node management | `add_node`, `add_branch_node`, `create_branch`, `delete_node`, `duplicate_node`, `rename_node`, `reorder_node`, `reorder_multiple_nodes`, `replace_node`, `replace_trigger_node`, `update_node_inputs`, `update_node_note`, `update_node_continue_on_error` |
|
|
187
|
+
| Discovery / testing | `list_node_types`, `get_node_specifications`, `get_dynamic_input_options`, `test_node`, `get_node_output` |
|
|
188
|
+
|
|
189
|
+
**The node catalog is huge** — live-enumerated 2026-08-31: **418 node types (58 triggers + 360 actions) across 56 applications.** The parts that matter most for this skill:
|
|
190
|
+
|
|
191
|
+
- **Softr-native triggers:** one-time + recurring schedules, `WEBHOOK` (inbound webhook), `SOFTR_EMAIL_RECEIVED` (inbound email! — delivery mechanism not captured), Softr Databases record events (added / updated / deleted / meets-conditions / enters-view / "run custom workflow on selected records"), Softr Apps events (Add Record form submitted, Edit Record form submitted, form submitted, user added, comment added) — and **"Run Custom Workflow action triggered"**, which by its name is the receiving end of the vibe block's `TRIGGER_CUSTOM_WORKFLOW` NavigationAction (name-based inference; the pairing has not been wired live). See SKILL.md's NavigationAction action-types list.
|
|
192
|
+
- **Softr-native actions:** `BRANCH`, `FILTER`, `WAIT`, `LOOP_ACTION_GROUP` (run each list item through the same steps), `SOFTR_SEND_EMAIL`, `CALL_API` (REST), `WEBPAGE_SCRAPPER`, `PDF_TO_TEXT`, `COMPRESS_FILES` (zip + download link), `TRANSFORM_DATA`, `RESPONDED_TO_WEBHOOK` (custom HTTP response to the webhook caller); Softr DB record CRUD incl. bulk update/delete and find; Softr Apps user management (find / create / delete / deactivate / activate / invite user, send push notification).
|
|
193
|
+
- **`CUSTOM_CODE`:** runs custom **JavaScript or Python** inside a workflow.
|
|
194
|
+
- **AI actions:** Softr AI, OpenAI, Anthropic, Gemini, and Mistral each ship Write / Summarize / Categorize / Custom-prompt nodes; OpenAI adds gpt-image-2 image generation. Pinecone, Firecrawl, Replicate, and Linkup nodes exist too.
|
|
195
|
+
- **Integration apps (top of 56):** Stripe (36 nodes), QuickBooks (24), ActiveCampaign (23), SharePoint (22), Asana (18), Gmail/Attio/Brevo/Resend (12 each), ClickUp/Zendesk (10), Airtable/Notion/Cal.com/HubSpot/Xero/DocuSign/Apollo (9 each), Sheets/Excel (8), monday/SQL/Jira (7), Slack/Telegram (6), plus Salesforce, Coda, Calendly, Twilio, Zoom, Linear, Trello, form tools (Typeform/Tally/Jotform/Fillout), and more.
|
|
196
|
+
|
|
197
|
+
**Mechanics from the server's own instructions:**
|
|
198
|
+
|
|
199
|
+
- Node inputs can embed **references to another node's runtime output**, a loop's current item, or named date/time tokens.
|
|
200
|
+
- **Test-first is mandated:** every testable node needs a test run before its outputs become referenceable by downstream nodes. Each node carries a `testRunMode` — `REAL_ONLY`, `MOCK_ONLY`, or `MOCK_AND_REAL` — so some nodes can only be tested against real side effects while others mock.
|
|
201
|
+
- **Workflows are workspace-level, not part of an app**: `preview_app` / `publish_app` do not apply. Link a workflow as `https://studio.softr.io/workflow/{workflowId}`.
|
|
202
|
+
|
|
203
|
+
**Why this matters to block work:** Softr Workflows are now the Softr-native answer to the "block writes to its own table, backend cascades the rest" pattern — for **Softr Database backends** what [airtable-automations.md](airtable-automations.md) is for Airtable backends. See the cross-table alternatives in [../datasources/writing.md](../datasources/writing.md#cross-table-operations).
|
|
204
|
+
|
|
205
|
+
## Per-application MCP servers
|
|
206
|
+
|
|
207
|
+
A separate product class from the workspace server (live-observed 2026-08-31 on two connected app servers): **one MCP server per published Softr app**, exposing that app's data to MCP clients. How these servers are provisioned/connected was not captured — check the app's settings in Studio or the official docs when setting one up.
|
|
208
|
+
|
|
209
|
+
**Tools (12):** `list_tables`, `describe_table`, `get_schema`, `get_records`, `get_record`, `get_linked_records`, `get_current_user`, `create_record`, `update_record`, `delete_record`, `batch_update_records`, `batch_delete_records`.
|
|
210
|
+
|
|
211
|
+
**Live-observed semantics:**
|
|
212
|
+
|
|
213
|
+
- **The table catalog is derived from the app itself.** `list_tables` returns only tables the app's pages actually use, each with an `operations` array (`read` / `create` / `update` / `delete`) mirroring the app's configured actions — read-only tables genuinely appear read-only. Each table also carries `context.pages` (which app pages use it) and `operationLabels` (the app's actual button labels: "Add record", "Edit", "Delete").
|
|
214
|
+
- An app may connect **multiple distinct data sources**; always call `list_tables` first for the full catalog before concluding data doesn't exist.
|
|
215
|
+
- `get_current_user` exists, and combined with the action-scoped catalog this implies the server operates in an **app-user context** rather than the builder identity the workspace server uses (inferred — not confirmed by a live `get_current_user` call).
|
|
216
|
+
- **Field keys in records are field IDs, not labels** — same rule as everywhere in Softr DB land; on these servers the mapping tool is `describe_table` (not `list_fields`).
|
|
217
|
+
- Its `get_schema` returns a **richer field-type catalog than the workspace server's**: creatable types add ADDRESS, PROGRESS, TIME, DATE_RANGE, BUTTON; SELECT documents `choices: array<{id, label, color}>` + `allowToAddNewChoice`; LONG_TEXT documents TEXT|HTML|MARKDOWN formats; ATTACHMENT documents `fileType` + `showAs PREVIEW|BADGE`; PERCENT documents `showAs NUMBER|PROGRESS_BAR|PROGRESS_RING`. Read-only list matches the workspace server.
|
|
218
|
+
- **Documented conventions** (unlike the workspace server's silent caps): pagination is `page` (1-based) + `pageSize` (max 100) with `total` and `hasMore` in the response; timestamps are UTC `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` for reads AND writes; errors return `{code, error, suggestion}` with machine-readable codes (NOT_FOUND, VALIDATION_ERROR, PERMISSION_DENIED, INVALID_REQUEST, INTERNAL_ERROR). Do not assume the workspace server's limits (200-record silent read cap, etc.) transfer here, or vice versa.
|
|
219
|
+
- Filter operators use the per-app naming (`GT`/`LT`/`GTE`/`LTE`, `DOES_NOT_CONTAINS`, `IS_WITHIN` relative dates, `IS_ONE_OF`/`IS_NONE_OF`, `HAS_ALL_OF`/`HAS_NONE_OF` (the latter flagged legacy in the schema), `INLINE_CONTAINS`) with per-operator supported-type lists — see the schema-drift caution in [Softr Database tools](#softr-database-tools).
|
|
220
|
+
|
|
221
|
+
**What they are NOT:** a delivery path for blocks. Per-app servers serve a published app's **data** at runtime; they cannot create, edit, or deploy vibe coding blocks — that stays on the workspace server.
|
|
222
|
+
|
|
136
223
|
## Two delivery paths for this skill
|
|
137
224
|
|
|
138
225
|
When generating a block, pick the delivery path by what's connected:
|
|
139
226
|
|
|
140
|
-
1. **MCP connected with Applications & Forms full access** — write the `.tsx` file locally first (it remains the source of truth and the reviewable artifact), then offer to deploy it directly: `create_vibe_coding_block` (or `update_vibe_coding_block_code` for edits), then `connect_vibe_coding_block_data_source` to wire up the data. Remember the action-permissions reset gotcha after every code push.
|
|
141
|
-
2. **No MCP (or read-only access)** — classic path: write the `.tsx` file and have the user paste it into Studio's Vibe Coding editor, then connect the data source in the **Source** tab themselves.
|
|
227
|
+
1. **WORKSPACE MCP server connected with Applications & Forms full access** (a per-application server does not count — it cannot create or deploy blocks; see the section above) — write the `.tsx` file locally first (it remains the source of truth and the reviewable artifact), then offer to deploy it directly: `create_vibe_coding_block` (or `update_vibe_coding_block_code` for edits), then `connect_vibe_coding_block_data_source` to wire up the data. Remember the action-permissions reset gotcha after every code push. After deploying, link the Studio page (`https://studio.softr.io/applications/{applicationId}/pages/{pageId}`) and offer `preview_app` / `publish_app` — publish only when asked, and mind the [preview-link auth warning](#application-management-tools).
|
|
228
|
+
2. **No workspace MCP (or read-only access)** — classic path: write the `.tsx` file and have the user paste it into Studio's Vibe Coding editor, then connect the data source in the **Source** tab themselves.
|
|
142
229
|
|
|
143
230
|
Either way, never deliver code inline in chat (JSX character corruption — see SKILL.md workflow step 5).
|
|
144
231
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Static Marketing Blocks — Heroes, Landing Sections, and the Editorial Lane
|
|
2
|
+
|
|
3
|
+
A **static block** is a Vibe Coding block with **zero datasources**: all content comes from editable settings, nothing loads, nothing mutates. Officially blessed — Softr's user guide lists "a static layout like a page header, pricing table, or content section" as a first-class use of the Vibe Coding block. Typical members: hero sections, landing-page headers, pricing tables, testimonial bands, logo walls, FAQ sections, footers, content/feature sections.
|
|
4
|
+
|
|
5
|
+
This archetype inverts most of the skill's data-block defaults, so read this file INSTEAD of the datasource guides when the requested block is static marketing content. (Verified against a Studio-AI-generated hero rendering live in Studio, 2026-08-31.)
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [Workflow deltas](#workflow-deltas)
|
|
10
|
+
- [Editorial baseline (replaces the Premium Visual Baseline)](#editorial-baseline-replaces-the-premium-visual-baseline)
|
|
11
|
+
- [Full-bleed layout license](#full-bleed-layout-license)
|
|
12
|
+
- [Full-viewport hero sizing](#full-viewport-hero-sizing)
|
|
13
|
+
- [Block-owned landing-page header](#block-owned-landing-page-header)
|
|
14
|
+
- [Section anchors on landing pages](#section-anchors-on-landing-pages)
|
|
15
|
+
- [Harvesting Studio-AI marketing blocks](#harvesting-studio-ai-marketing-blocks)
|
|
16
|
+
|
|
17
|
+
## Workflow deltas
|
|
18
|
+
|
|
19
|
+
When the block is static marketing content:
|
|
20
|
+
|
|
21
|
+
- **SKIP data-source interrogation entirely** — no data source type question, no field IDs, no datasource guide loading. Step 1 (DESIGN.md brand detection) still runs unchanged.
|
|
22
|
+
- **Settings-first design is the default** — every user-visible string, image, and link becomes an editable setting; see the granularity doctrine in [editable-settings.md](editable-settings.md#granularity-doctrine-settings-first-static-blocks). This is what makes the block a reusable, client-editable template instead of frozen copy.
|
|
23
|
+
- **Self-validation deltas** — these Step 6 checklist items DON'T apply: loading/error/empty states (nothing loads), `getFieldValue()` wrapping (no records), mutation `enabled` gating, `fetchNextPage` rules, container/content wrappers (see full-bleed below). These DO apply, plus extras: hooks before conditional returns, module-scope sub-components, no hardcoded user-visible copy, media settings gated for the empty-src state, array rows keyed by index, `// BLOCK PLACEMENT:` comment present.
|
|
24
|
+
- **Ask-AI buttons still need a data source.** The one place a "static" block touches data: an `OPEN_CHAT` NavigationAction pulls AI context from the block that triggered it — with no data source connected, `chat/prepare` returns HTTP 500. Connect the block to whatever table the AI should read, even though the block renders none of it. See [anti-patterns.md](anti-patterns.md#layout--styling).
|
|
25
|
+
|
|
26
|
+
## Editorial baseline (replaces the Premium Visual Baseline)
|
|
27
|
+
|
|
28
|
+
SKILL.md's Premium Visual Baseline (gradient wrapper, icon-in-rounded-square header, card grids, skeletons, empty states) is an **app-UI** baseline — applying it to a hero produces a dashboard cosplaying as a landing page. For static marketing blocks, the baseline is editorial instead:
|
|
29
|
+
|
|
30
|
+
1. **Typographic hierarchy carries the design** — display-size headings, an eyebrow/tagline, generous line-height on body copy. Use the marketing display-type lane in ui-ux-guidelines.md §6.
|
|
31
|
+
2. **Brand-exact values over theme tokens** — arbitrary Tailwind values (`bg-[#FAF5EC]`, `text-[17px]`, `rounded-[6px]`) and inline-style brand hexes are the correct tool here (see §7's editorial lane in ui-ux-guidelines.md). Hoist repeated hexes to module-scope constants — see [anti-patterns.md](anti-patterns.md#layout--styling).
|
|
32
|
+
3. **Decorative layers, used deliberately** — background blobs/shapes, edge-fade image masks, art-directed responsive images. Recipes in [common-patterns.md](common-patterns.md).
|
|
33
|
+
4. **No loading/empty/error scaffolding** — there is nothing to load. The only "empty" state to handle is an un-uploaded media setting ([editable-settings.md](editable-settings.md#media-hooks-useimagesetting-and-usevideosetting)).
|
|
34
|
+
5. **Restraint still applies** — ui-ux-guidelines.md's AI-slop bans (purple-to-blue gradients, glassmorphism everywhere, gradient text) bind in this lane too. Editorial ≠ decorated.
|
|
35
|
+
|
|
36
|
+
## Full-bleed layout license
|
|
37
|
+
|
|
38
|
+
**`container`/`content` wrappers are optional platform behavior, not platform-enforced.** Per the official developer guide: "By default, block occupies full width of the page but special classes - `container` and `content` are **available** to constrain the width [of content to match app's max width settings]" (emphasis added). The wrappers remain the strong default for app/content blocks that sit next to native blocks (width consistency — that's their whole purpose). Static marketing blocks legitimately skip them so backgrounds, images, and decorative shapes run edge-to-edge.
|
|
39
|
+
|
|
40
|
+
When you go full-bleed:
|
|
41
|
+
|
|
42
|
+
- **Own your horizontal gutters** — the responsive padding ramp `px-6 md:px-12 lg:px-16` on the content container is the Studio-verified shape.
|
|
43
|
+
- **Own your inner max-widths** — constrain the copy column yourself (`lg:max-w-[46%]`, `max-w-[430px]` on body text).
|
|
44
|
+
- **`overflow-hidden` on the block root** if decorative shapes offset off-canvas (prevents horizontal scroll).
|
|
45
|
+
- **Record the choice** in the placement comment so future edits and reviews know it's deliberate:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// BLOCK PLACEMENT: full-bleed hero, native header hidden, owns all spacing
|
|
49
|
+
// Spacing: no container/content; gutters px-6 md:px-12 lg:px-16
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The standard spacing table (`py-3 px-8` wrappers) does not apply — a full-bleed block owns all of its spacing.
|
|
53
|
+
|
|
54
|
+
## Full-viewport hero sizing
|
|
55
|
+
|
|
56
|
+
The Studio-verified responsive shape:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<div className="relative min-h-screen lg:min-h-0 lg:h-screen lg:flex lg:flex-col ...">
|
|
60
|
+
<main className="lg:flex-1 lg:flex lg:items-center ...">
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Natural height on mobile, locked viewport height on desktop, content vertically centered in the remaining space. Rules and gotchas:
|
|
64
|
+
|
|
65
|
+
- **vh/`h-screen` resolve against the real viewport** (blocks are shadow DOM in the main document — see [Block-owned landing-page header](#block-owned-landing-page-header)). So `h-screen` fills the window only when the native header is hidden on that page; with a native header above, a 100vh block overflows by the header height. Use `min-h-[calc(100vh-<headerHeight>px)]` when native chrome stays.
|
|
66
|
+
- **Hard `h-screen` + `overflow-hidden` + centered flex CLIPS settings-grown content — unrecoverably.** Every string in a settings-first hero is builder-lengthenable; on a short laptop viewport, two extra sentences in the description push the CTA row past the clip, and centered-flex overflow clips top AND bottom, so no scrolling reaches it. Prefer `lg:min-h-screen` (grow-with-content) unless the locked-viewport look is explicitly wanted; if locking, flag the tradeoff to the user and keep the copy settings short.
|
|
67
|
+
- **Mobile URL-bar resize**: consider `min-h-[100svh]`/`dvh` variants if the mobile jump matters; the `min-h-screen` mobile default is the safe baseline.
|
|
68
|
+
|
|
69
|
+
## Block-owned landing-page header
|
|
70
|
+
|
|
71
|
+
The scope rule stays true: Softr's **native** chrome (top bar, auth-aware account menu, user-group-gated nav) cannot be built or replaced as a block — restyling it is global Custom Code territory ([native-chrome-styling.md](native-chrome-styling.md)). But on a landing page where the native header is **hidden in Studio**, a hero block CAN render its own `<header>` — Softr's own Studio AI emits exactly this, and the official user guide lists "a page header" as a supported static layout. Mechanics (rendered live in Studio 2026-08-31; the positioning behavior is standard shadow-DOM/CSS — confirm in the published app):
|
|
72
|
+
|
|
73
|
+
- **`position: fixed` works from inside the block** and anchors to the viewport — blocks render in a shadow root in the MAIN document (not an iframe), and shadow roots don't create a containing block. The header escapes the block's visual bounds and overlays every other block on the page.
|
|
74
|
+
- **Window scroll listeners work normally** — `window.scrollY` reflects the real page. The scroll-condensing treatment (translucent bg + backdrop-blur + border after ~24px) is in [common-patterns.md](common-patterns.md#scroll-condensing-fixed-header-landing-page-hero).
|
|
75
|
+
|
|
76
|
+
The caveat set — state these in any block that ships its own header:
|
|
77
|
+
|
|
78
|
+
1. **Per-page only.** The header exists solely on pages containing this block. Every page of a multi-page site needs either this block or the native header, or navigation disappears.
|
|
79
|
+
2. **No auth-aware nav logic.** You forfeit Softr's account menu and user-group-gated items — nav items are plain `<NavigationAction>`s. Fine for public landing pages; wrong for logged-in app pages. For the login CTA, use the auth-aware swap (`useCurrentUser()` → "Sign in" vs "Dashboard") from [common-patterns.md](common-patterns.md#auth-aware-header-cta).
|
|
80
|
+
3. **Don't ship both headers on one page.** The z-index outcome against a visible native `#topbar-root` (sticky, main document) is untested — hide the native header on this page, or don't use the pattern.
|
|
81
|
+
4. **Keep transforms/filters off the header's ancestors.** `position: fixed` anchors to the viewport ONLY while no ancestor establishes a containing block — a `transform`, `filter`, `perspective`, or `will-change` on the block root (e.g. an entrance animation) silently converts the "fixed" header to absolute and lets `overflow-hidden` clip it. Leaf-element transforms (`hover:-translate-y-[1px]` on buttons) are fine. Verify in the **published app**, not just the Studio canvas.
|
|
82
|
+
5. **Mobile nav is mandatory.** `hidden md:flex` on the nav with no fallback means nav items simply don't exist on phones (Studio AI ships exactly this bug). Pair the pattern with a shadcn `Sheet`-based mobile menu (hamburger → drawer listing the same `navItems` array) — the component is on the platform roster.
|
|
83
|
+
6. **Landmark hygiene.** Shadow DOM does NOT hide landmarks from assistive tech, and Softr's native chrome uses semantic elements. On a page with native chrome visible, a block's own `<header>`/`<main>` creates duplicate banner/main landmarks — use plain `<div>`s there. Reserve `<header>`/`<main>` for pages where the native chrome is hidden and the block genuinely IS the page chrome.
|
|
84
|
+
|
|
85
|
+
The wider decision (restyle native vs replace globally vs block-owned) is laid out in [native-chrome-styling.md](native-chrome-styling.md#restyle-vs-replace-vs-block-owned-header).
|
|
86
|
+
|
|
87
|
+
## Section anchors on landing pages
|
|
88
|
+
|
|
89
|
+
Heroes pair with same-page sections via hash links (nav "Capabilities" → `#capabilities` further down).
|
|
90
|
+
|
|
91
|
+
- **Write anchor destinations RELATIVE**, per Hard Constraint 20: `/#capabilities` or `/page#section`. A Studio-generated hero shipped its CTA configured as the absolute self-domain form (`https://<app>.softr.app/#capabilities`, observed in the Settings pane 2026-08-31) — rewrite that form; hardcoded domains break on custom-domain publish and between staging/production.
|
|
92
|
+
- **A URL fragment cannot target an element inside a Vibe block's shadow root** — fragment lookup stops at the shadow boundary (same family as the documented `getElementById` anti-pattern). Anchors must target something in the main document: the block/section host. Softr has a native anchor-link mechanism targeting blocks — **verify live which fragment a Vibe Coding block answers to** before promising exact syntax to a user.
|
|
93
|
+
- **In-block scrolling** (scroll to a section inside the same block) uses refs + `scrollIntoView` with the `setTimeout(fn, 0)` rule (Hard Constraint 17), not fragments.
|
|
94
|
+
|
|
95
|
+
## Harvesting Studio-AI marketing blocks
|
|
96
|
+
|
|
97
|
+
Marketing blocks are where you'll most often mine Studio-AI output for platform truth (it surfaced `useLongTextSetting` and the `navigation` array-schema type before the official docs did). Mine capabilities, never copy verbatim — the standard defects to fix on adoption are listed in SKILL.md's "Platform truth sources" note and [softr-mcp.md](softr-mcp.md#adopting-studio-ai-generated-code) (React keys, hex drift, formatting, missing mobile nav, unconditional media renders).
|
package/ui-ux-guidelines.md
CHANGED
|
@@ -192,6 +192,17 @@ Aim for at least **1.25x ratio** between adjacent scale steps. Sizes only 1-2px
|
|
|
192
192
|
- Use `text-foreground`, not pure black. The theme token is calibrated for comfortable contrast.
|
|
193
193
|
- **Use tabular numbers** in data tables for column alignment: add `style={{ fontVariantNumeric: "tabular-nums" }}` to numeric cells.
|
|
194
194
|
|
|
195
|
+
### Marketing / editorial display type (heroes, landing blocks)
|
|
196
|
+
|
|
197
|
+
The scale above is for **app UI** (dashboards, forms, lists). Static marketing blocks get a sanctioned exception lane (patterns verified from rendering Studio-AI hero output, 2026-08-31 — see [references/static-blocks.md](references/static-blocks.md)):
|
|
198
|
+
|
|
199
|
+
- **Display headings 40–60px**, stepped per breakpoint with arbitrary px classes — `text-[40px] md:text-[56px] lg:text-[54px] xl:text-[60px]` — px precision because the Tailwind scale jumps too coarsely up there.
|
|
200
|
+
- **Display line-height 1.05–1.1** for multi-line display headings (`leading-[1.08]`); the leading-snug rule above is for app-scale headings.
|
|
201
|
+
- **Slight negative tracking on large display type**: `tracking-[-0.015em]`.
|
|
202
|
+
- **Eyebrow/tagline pattern**: 13–14px, uppercase, `tracking-[0.15em]`–`tracking-[0.2em]`, `font-medium`, in an accent color, above the heading. (An eyebrow IS a "short label" — the all-caps rule below permits it.)
|
|
203
|
+
- **Wordmark/logotype text**: `font-light`/`font-normal` + wide tracking (`tracking-[0.16em]`) for luxury brands.
|
|
204
|
+
- Body copy in this lane still follows the body rules (16–19px, `leading-[1.6]`-ish, max-width constrained).
|
|
205
|
+
|
|
195
206
|
### Typography Anti-Patterns:
|
|
196
207
|
- **DO NOT** use monospace typography as lazy shorthand for "technical/developer" vibes.
|
|
197
208
|
- **DO NOT** place large rounded icons above every heading — they rarely add value and look templated.
|
|
@@ -220,7 +231,7 @@ All spacing uses **multiples of 4px**. The critical step that pure 8pt systems m
|
|
|
220
231
|
- **Vary spacing for hierarchy.** A heading with extra space above reads as more important. Uniform padding everywhere feels monotonous.
|
|
221
232
|
- **Internal spacing <= external spacing:** Padding *inside* a component must be smaller than the margin *between* components.
|
|
222
233
|
- **Button height:** 40px (`h-10`) or 48px (`h-12`) — touch-friendly.
|
|
223
|
-
- **
|
|
234
|
+
- **Stick to the Tailwind scale for spacing in app-UI blocks** — avoid arbitrary spacing values like `p-[13px]` there; scale steps keep rhythm consistent. This is a design-consistency preference, NOT a platform limit: the platform's Tailwind build is JIT and the full arbitrary-value syntax compiles, including opacity-modified arbitrary hex (`bg-[#FAF5EC]/85`), negatives (`-top-[22%]`), arbitrary object-position/z/vw (verified from rendering Studio-AI output, 2026-08-31). In the **editorial/brand-faithful lane** (marketing heroes, luxury type, brand-exact radii — see [references/static-blocks.md](references/static-blocks.md)), px-precise arbitrary values are the correct tool. Elsewhere in this file, `min-h-[44px]` touch targets already use them.
|
|
224
235
|
- **Generous whitespace** reduces cognitive load — it is not wasted space.
|
|
225
236
|
|
|
226
237
|
### Depth and Elevation:
|
|
@@ -306,7 +317,7 @@ Vestibular disorders affect ~35% of adults over 40. Always respect `prefers-redu
|
|
|
306
317
|
| State | Purpose | Implementation |
|
|
307
318
|
|---|---|---|
|
|
308
319
|
| **Default** | Ready to use | Standard `Button` component |
|
|
309
|
-
| **Hover** | Cursor over it | Handled by shadcn `Button` |
|
|
320
|
+
| **Hover** | Cursor over it | Handled by shadcn `Button` — EXCEPT when an inline `style={{ backgroundColor }}` overrides the variant: inline style beats every `hover:bg-*` class, so use `hover:opacity-90` / transform instead (see SKILL.md's brand-hex Button recipe) |
|
|
310
321
|
| **Focus** | Keyboard-selected | `focus-visible:ring-2` — never remove |
|
|
311
322
|
| **Active** | Being pressed | Pressed in, darker |
|
|
312
323
|
| **Loading** | Action in progress | Replace label with `<Spinner />` + disable |
|
|
@@ -535,6 +546,8 @@ Avoid the "hero metric layout template" — big number, small label, supporting
|
|
|
535
546
|
### Keyboard and screen reader:
|
|
536
547
|
- All interactive elements reachable by keyboard (Tab, Enter, Space, Escape).
|
|
537
548
|
- `aria-label` on all icon-only buttons.
|
|
549
|
+
- **`aria-hidden="true"` on decorative glyph characters** used as separators or ornaments (●, •, |, →, ✦) — screen readers otherwise announce them literally ("black circle"). Purely decorative empty divs need nothing; decorative `<img>` elements need `alt=""`.
|
|
550
|
+
- **Landmark hygiene when a block ships page chrome**: shadow DOM does NOT hide landmarks from assistive tech, and Softr's native chrome uses semantic elements — so a block's own `<header>`/`<main>` on a page with native chrome creates duplicate banner/main landmarks. Use plain `<div>`s there; reserve `<header>`/`<main>` for pages where the native chrome is hidden.
|
|
538
551
|
- Semantic HTML: `<button>` for actions, `<a>` for navigation, `<input>` for data.
|
|
539
552
|
- **Focus rings:** Never `outline: none` without replacement. Always keep `focus-visible:ring-2`. Focus ring must be 2-3px thick, high contrast, offset from the element.
|
|
540
553
|
- Tables: proper `<thead>`, `<tbody>`, `<th scope="col">`.
|
|
@@ -551,6 +564,9 @@ Avoid the "hero metric layout template" — big number, small label, supporting
|
|
|
551
564
|
- Form fields: `w-full` on mobile.
|
|
552
565
|
- Navigation: collapse to hamburger or bottom nav on small screens.
|
|
553
566
|
|
|
567
|
+
### Art-directed responsive images:
|
|
568
|
+
A settings hook returns a plain value, so one `useImageSetting` may safely feed **two sibling `<img>` renders** with opposite visibility classes — desktop: absolute-positioned, masked, cropped via arbitrary `object-[x%_y%]`; mobile: in-flow full-bleed (`hidden lg:block` / `lg:hidden`, same mechanism as the table→cards swap in §18). The builder still edits ONE image in the Settings pane. To bleed the mobile render edge-to-edge against the wrapper's own horizontal padding, use matching negative margins (`-mx-6` against `px-6`, `md:-mx-12` against `md:px-12`).
|
|
569
|
+
|
|
554
570
|
### Breakpoint strategy:
|
|
555
571
|
```
|
|
556
572
|
Mobile first: default -> sm (640px) -> md (768px) -> lg (1024px) -> xl (1280px)
|