@bettercms-ai/kit 1.0.0 → 2.0.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/README.md +33 -0
- package/dist/adapters/dock-values.d.ts +28 -0
- package/dist/adapters/dock-values.js +41 -0
- package/dist/adapters/dock-values.js.map +1 -0
- package/{adapters → dist/adapters}/icons.js +2 -2
- package/dist/adapters/icons.js.map +1 -0
- package/{adapters → dist/adapters}/lossy.d.ts +90 -31
- package/dist/adapters/lossy.js +414 -0
- package/dist/adapters/lossy.js.map +1 -0
- package/{adapters → dist/adapters}/prod-types.d.ts +82 -4
- package/{adapters → dist/adapters}/prod-types.js +71 -3
- package/dist/adapters/prod-types.js.map +1 -0
- package/{adapters → dist/adapters}/schema.d.ts +40 -1
- package/{adapters → dist/adapters}/schema.js +996 -179
- package/dist/adapters/schema.js.map +1 -0
- package/{adapters → dist/adapters}/values.d.ts +26 -5
- package/{adapters → dist/adapters}/values.js +87 -17
- package/dist/adapters/values.js.map +1 -0
- package/dist/brand/logo.js +96 -0
- package/dist/brand/logo.js.map +1 -0
- package/{context.d.ts → dist/context.d.ts} +92 -2
- package/{context.js → dist/context.js} +5 -2
- package/dist/context.js.map +1 -0
- package/{controls → dist/controls}/button.d.ts +19 -5
- package/{controls → dist/controls}/button.js +125 -54
- package/dist/controls/button.js.map +1 -0
- package/{controls → dist/controls}/c1.js +1 -1
- package/dist/controls/c1.js.map +1 -0
- package/{controls → dist/controls}/c2.js +1 -1
- package/dist/controls/c2.js.map +1 -0
- package/{controls → dist/controls}/choice.d.ts +7 -2
- package/{controls → dist/controls}/choice.js +71 -18
- package/dist/controls/choice.js.map +1 -0
- package/{controls → dist/controls}/code-register.js +1 -1
- package/dist/controls/code-register.js.map +1 -0
- package/{controls → dist/controls}/code.js +10 -9
- package/dist/controls/code.js.map +1 -0
- package/{controls → dist/controls}/color.js +8 -8
- package/dist/controls/color.js.map +1 -0
- package/{controls → dist/controls}/component-services.d.ts +42 -0
- package/dist/controls/component-services.js +7 -0
- package/dist/controls/component-services.js.map +1 -0
- package/{controls → dist/controls}/date-register.js +1 -1
- package/dist/controls/date-register.js.map +1 -0
- package/{controls → dist/controls}/date-zone.js +8 -2
- package/dist/controls/date-zone.js.map +1 -0
- package/{controls → dist/controls}/date.js +32 -24
- package/dist/controls/date.js.map +1 -0
- package/{controls → dist/controls}/form-picker-register.js +1 -1
- package/dist/controls/form-picker-register.js.map +1 -0
- package/{controls → dist/controls}/form-picker.js +11 -9
- package/dist/controls/form-picker.js.map +1 -0
- package/{controls → dist/controls}/icon-register.js +1 -1
- package/dist/controls/icon-register.js.map +1 -0
- package/{controls → dist/controls}/icon.d.ts +4 -2
- package/{controls → dist/controls}/icon.js +32 -27
- package/dist/controls/icon.js.map +1 -0
- package/{controls → dist/controls}/index.d.ts +1 -1
- package/{controls → dist/controls}/index.js +4 -3
- package/dist/controls/index.js.map +1 -0
- package/{controls → dist/controls}/json-register.js +1 -1
- package/dist/controls/json-register.js.map +1 -0
- package/{controls → dist/controls}/json.js +13 -3
- package/dist/controls/json.js.map +1 -0
- package/{controls → dist/controls}/link.js +6 -6
- package/dist/controls/link.js.map +1 -0
- package/{controls → dist/controls}/location-register.js +1 -1
- package/dist/controls/location-register.js.map +1 -0
- package/{controls → dist/controls}/location.js +64 -40
- package/dist/controls/location.js.map +1 -0
- package/{controls → dist/controls}/media-register.js +1 -1
- package/dist/controls/media-register.js.map +1 -0
- package/{controls → dist/controls}/media-services.d.ts +6 -0
- package/dist/controls/media-services.js +0 -0
- package/{controls → dist/controls}/media.d.ts +35 -4
- package/{controls → dist/controls}/media.js +491 -171
- package/dist/controls/media.js.map +1 -0
- package/{controls → dist/controls}/number.js +10 -9
- package/dist/controls/number.js.map +1 -0
- package/{controls → dist/controls}/reference-register.js +1 -1
- package/dist/controls/reference-register.js.map +1 -0
- package/{controls → dist/controls}/reference-services.d.ts +4 -0
- package/dist/controls/reference-services.js +0 -0
- package/{controls → dist/controls}/reference.d.ts +1 -1
- package/{controls → dist/controls}/reference.js +93 -77
- package/dist/controls/reference.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +49 -0
- package/{controls → dist/controls}/rich-text/blocks.js +471 -144
- package/dist/controls/rich-text/blocks.js.map +1 -0
- package/{controls → dist/controls}/rich-text/catalog.d.ts +4 -1
- package/{controls → dist/controls}/rich-text/catalog.js +75 -65
- package/dist/controls/rich-text/catalog.js.map +1 -0
- package/{controls → dist/controls}/rich-text/codec.d.ts +9 -1
- package/{controls → dist/controls}/rich-text/codec.js +3 -1
- package/dist/controls/rich-text/codec.js.map +1 -0
- package/{controls → dist/controls}/rich-text/component-card.d.ts +1 -1
- package/{controls → dist/controls}/rich-text/component-card.js +417 -99
- package/dist/controls/rich-text/component-card.js.map +1 -0
- package/dist/controls/rich-text/component-panel.d.ts +47 -0
- package/dist/controls/rich-text/component-panel.js +141 -0
- package/dist/controls/rich-text/component-panel.js.map +1 -0
- package/{controls → dist/controls}/rich-text/control.d.ts +1 -2
- package/{controls → dist/controls}/rich-text/control.js +84 -42
- package/dist/controls/rich-text/control.js.map +1 -0
- package/{controls → dist/controls}/rich-text/dom.d.ts +52 -9
- package/{controls → dist/controls}/rich-text/dom.js +379 -50
- package/dist/controls/rich-text/dom.js.map +1 -0
- package/{controls → dist/controls}/rich-text/editor.d.ts +7 -0
- package/{controls → dist/controls}/rich-text/editor.js +148 -48
- package/dist/controls/rich-text/editor.js.map +1 -0
- package/{controls → dist/controls}/rich-text/floating.d.ts +6 -1
- package/{controls → dist/controls}/rich-text/floating.js +35 -18
- package/dist/controls/rich-text/floating.js.map +1 -0
- package/{controls → dist/controls}/rich-text/index.js +1 -1
- package/dist/controls/rich-text/index.js.map +1 -0
- package/dist/controls/rich-text/link-form.js +147 -0
- package/dist/controls/rich-text/link-form.js.map +1 -0
- package/{controls → dist/controls}/rich-text/markdown.js +4 -2
- package/dist/controls/rich-text/markdown.js.map +1 -0
- package/{controls → dist/controls}/rich-text/ops.js +17 -15
- package/dist/controls/rich-text/ops.js.map +1 -0
- package/dist/controls/rich-text/plain.d.ts +17 -0
- package/dist/controls/rich-text/plain.js +44 -0
- package/dist/controls/rich-text/plain.js.map +1 -0
- package/{controls → dist/controls}/rich-text/toolbar.d.ts +6 -2
- package/{controls → dist/controls}/rich-text/toolbar.js +12 -7
- package/dist/controls/rich-text/toolbar.js.map +1 -0
- package/{controls → dist/controls}/scale.js +5 -5
- package/dist/controls/scale.js.map +1 -0
- package/{controls → dist/controls}/slot-register.js +1 -1
- package/dist/controls/slot-register.js.map +1 -0
- package/{controls → dist/controls}/slot-services.d.ts +25 -0
- package/dist/controls/slot-services.js +155 -0
- package/dist/controls/slot-services.js.map +1 -0
- package/{controls → dist/controls}/slot.js +42 -24
- package/dist/controls/slot.js.map +1 -0
- package/{controls → dist/controls}/table/index.js +1 -1
- package/dist/controls/table/index.js.map +1 -0
- package/{controls → dist/controls}/table/prod.js +3 -3
- package/dist/controls/table/prod.js.map +1 -0
- package/{controls → dist/controls}/table/refs.js +13 -2
- package/dist/controls/table/refs.js.map +1 -0
- package/{controls → dist/controls}/table/stored.js +1 -1
- package/dist/controls/table/stored.js.map +1 -0
- package/{controls → dist/controls}/table/table-editor.js +51 -48
- package/dist/controls/table/table-editor.js.map +1 -0
- package/{controls → dist/controls}/table/table-link.js +3 -3
- package/dist/controls/table/table-link.js.map +1 -0
- package/{controls → dist/controls}/table/table.js +3 -3
- package/dist/controls/table/table.js.map +1 -0
- package/{controls → dist/controls}/text.d.ts +2 -2
- package/{controls → dist/controls}/text.js +78 -43
- package/dist/controls/text.js.map +1 -0
- package/dist/controls/unknown-value.d.ts +17 -0
- package/dist/controls/unknown-value.js +66 -0
- package/dist/controls/unknown-value.js.map +1 -0
- package/dist/core/core.d.ts +63 -0
- package/dist/core/core.js +171 -0
- package/dist/core/core.js.map +1 -0
- package/dist/core/facets.d.ts +347 -0
- package/dist/core/facets.js +334 -0
- package/dist/core/facets.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/{engine → dist/engine}/array.js +34 -21
- package/dist/engine/array.js.map +1 -0
- package/{engine → dist/engine}/bits.js +1 -1
- package/dist/engine/bits.js.map +1 -0
- package/{engine → dist/engine}/breadcrumb.d.ts +3 -3
- package/{engine → dist/engine}/breadcrumb.js +12 -36
- package/dist/engine/breadcrumb.js.map +1 -0
- package/{engine → dist/engine}/expand.d.ts +2 -4
- package/{engine → dist/engine}/expand.js +5 -22
- package/dist/engine/expand.js.map +1 -0
- package/{engine → dist/engine}/field-list.js +1 -1
- package/dist/engine/field-list.js.map +1 -0
- package/{engine → dist/engine}/field-shell.js +249 -121
- package/dist/engine/field-shell.js.map +1 -0
- package/{engine → dist/engine}/frame.d.ts +3 -1
- package/{engine → dist/engine}/frame.js +16 -14
- package/dist/engine/frame.js.map +1 -0
- package/{engine → dist/engine}/group-summary.d.ts +2 -1
- package/{engine → dist/engine}/group-summary.js +7 -3
- package/dist/engine/group-summary.js.map +1 -0
- package/{engine → dist/engine}/grouping.d.ts +19 -5
- package/{engine → dist/engine}/grouping.js +193 -21
- package/dist/engine/grouping.js.map +1 -0
- package/{engine → dist/engine}/item-list.d.ts +11 -1
- package/{engine → dist/engine}/item-list.js +35 -13
- package/dist/engine/item-list.js.map +1 -0
- package/{engine → dist/engine}/kit-field.js +45 -10
- package/dist/engine/kit-field.js.map +1 -0
- package/{engine → dist/engine}/schema-form.d.ts +27 -2
- package/{engine → dist/engine}/schema-form.js +182 -40
- package/dist/engine/schema-form.js.map +1 -0
- package/{engine → dist/engine}/slot.js +5 -15
- package/dist/engine/slot.js.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/{kit.css → dist/kit.css} +19 -0
- package/{lib → dist/lib}/cn.js +7 -4
- package/dist/lib/cn.js.map +1 -0
- package/{lib → dist/lib}/columns.js +4 -1
- package/dist/lib/columns.js.map +1 -0
- package/{lib → dist/lib}/frames.js +2 -2
- package/dist/lib/frames.js.map +1 -0
- package/dist/lib/icon-image.d.ts +10 -0
- package/dist/lib/icon-image.js +8 -0
- package/dist/lib/icon-image.js.map +1 -0
- package/{lib → dist/lib}/icons.js +4 -3
- package/dist/lib/icons.js.map +1 -0
- package/dist/lib/input-modality.d.ts +11 -0
- package/dist/lib/input-modality.js +47 -0
- package/dist/lib/input-modality.js.map +1 -0
- package/dist/lib/key-event.d.ts +11 -0
- package/dist/lib/key-event.js +16 -0
- package/dist/lib/key-event.js.map +1 -0
- package/{lib → dist/lib}/path.js +4 -2
- package/dist/lib/path.js.map +1 -0
- package/{lib → dist/lib}/schema.d.ts +34 -3
- package/{lib → dist/lib}/schema.js +110 -13
- package/dist/lib/schema.js.map +1 -0
- package/{lib → dist/lib}/utils.js +1 -1
- package/dist/lib/utils.js.map +1 -0
- package/{lib → dist/lib}/validate.d.ts +11 -1
- package/{lib → dist/lib}/validate.js +85 -11
- package/dist/lib/validate.js.map +1 -0
- package/dist/library/analysis.d.ts +23 -0
- package/dist/library/analysis.js +160 -0
- package/dist/library/analysis.js.map +1 -0
- package/dist/library/catalog.d.ts +57 -0
- package/dist/library/catalog.js +307 -0
- package/dist/library/catalog.js.map +1 -0
- package/dist/library/coreNotes.d.ts +8 -0
- package/dist/library/coreNotes.js +111 -0
- package/dist/library/coreNotes.js.map +1 -0
- package/dist/library/coverage.d.ts +40 -0
- package/dist/library/coverage.js +59 -0
- package/dist/library/coverage.js.map +1 -0
- package/dist/library/entries/advanced.entries.d.ts +2 -0
- package/dist/library/entries/advanced.entries.js +6579 -0
- package/dist/library/entries/advanced.entries.js.map +1 -0
- package/dist/library/entries/commerce.entries.d.ts +2 -0
- package/dist/library/entries/commerce.entries.js +4140 -0
- package/dist/library/entries/commerce.entries.js.map +1 -0
- package/dist/library/entries/company.entries.d.ts +2 -0
- package/dist/library/entries/company.entries.js +2731 -0
- package/dist/library/entries/company.entries.js.map +1 -0
- package/dist/library/entries/conversion.entries.d.ts +2 -0
- package/dist/library/entries/conversion.entries.js +3707 -0
- package/dist/library/entries/conversion.entries.js.map +1 -0
- package/dist/library/entries/data.entries.d.ts +7 -0
- package/dist/library/entries/data.entries.js +4613 -0
- package/dist/library/entries/data.entries.js.map +1 -0
- package/dist/library/entries/events.entries.d.ts +2 -0
- package/dist/library/entries/events.entries.js +3428 -0
- package/dist/library/entries/events.entries.js.map +1 -0
- package/dist/library/entries/features.entries.d.ts +2 -0
- package/dist/library/entries/features.entries.js +3157 -0
- package/dist/library/entries/features.entries.js.map +1 -0
- package/dist/library/entries/heroes.entries.d.ts +2 -0
- package/dist/library/entries/heroes.entries.js +3401 -0
- package/dist/library/entries/heroes.entries.js.map +1 -0
- package/dist/library/entries/index.d.ts +3 -0
- package/dist/library/entries/index.js +35 -0
- package/dist/library/entries/index.js.map +1 -0
- package/dist/library/entries/listings.entries.d.ts +2 -0
- package/dist/library/entries/listings.entries.js +2976 -0
- package/dist/library/entries/listings.entries.js.map +1 -0
- package/dist/library/entries/longform.entries.d.ts +2 -0
- package/dist/library/entries/longform.entries.js +4227 -0
- package/dist/library/entries/longform.entries.js.map +1 -0
- package/dist/library/entries/media.entries.d.ts +2 -0
- package/dist/library/entries/media.entries.js +2781 -0
- package/dist/library/entries/media.entries.js.map +1 -0
- package/dist/library/entries/navigation.entries.d.ts +2 -0
- package/dist/library/entries/navigation.entries.js +4011 -0
- package/dist/library/entries/navigation.entries.js.map +1 -0
- package/dist/library/entries/pricing.entries.d.ts +2 -0
- package/dist/library/entries/pricing.entries.js +2383 -0
- package/dist/library/entries/pricing.entries.js.map +1 -0
- package/dist/library/entries/social-proof.entries.d.ts +2 -0
- package/dist/library/entries/social-proof.entries.js +2297 -0
- package/dist/library/entries/social-proof.entries.js.map +1 -0
- package/dist/library/examples/bento.d.ts +92 -0
- package/dist/library/examples/bento.js +201 -0
- package/dist/library/examples/bento.js.map +1 -0
- package/dist/library/examples/blocks.d.ts +14 -0
- package/dist/library/examples/blocks.js +313 -0
- package/dist/library/examples/blocks.js.map +1 -0
- package/dist/library/examples/faq.d.ts +28 -0
- package/dist/library/examples/faq.js +230 -0
- package/dist/library/examples/faq.js.map +1 -0
- package/dist/library/examples/hero.d.ts +43 -0
- package/dist/library/examples/hero.js +222 -0
- package/dist/library/examples/hero.js.map +1 -0
- package/dist/library/examples/index.d.ts +36 -0
- package/dist/library/examples/index.js +109 -0
- package/dist/library/examples/index.js.map +1 -0
- package/dist/library/examples/journalStory.d.ts +93 -0
- package/dist/library/examples/journalStory.js +338 -0
- package/dist/library/examples/journalStory.js.map +1 -0
- package/dist/library/examples/megaMenu.d.ts +134 -0
- package/dist/library/examples/megaMenu.js +442 -0
- package/dist/library/examples/megaMenu.js.map +1 -0
- package/dist/library/examples/pricing.d.ts +103 -0
- package/dist/library/examples/pricing.js +479 -0
- package/dist/library/examples/pricing.js.map +1 -0
- package/dist/library/examples/tabs.d.ts +87 -0
- package/dist/library/examples/tabs.js +180 -0
- package/dist/library/examples/tabs.js.map +1 -0
- package/dist/library/export.d.ts +42 -0
- package/dist/library/export.js +80 -0
- package/dist/library/export.js.map +1 -0
- package/dist/library/index.d.ts +13 -0
- package/dist/library/index.js +6 -0
- package/dist/library/kit-catalog.json +1510 -0
- package/dist/library/types.d.ts +42 -0
- package/dist/library/types.js +0 -0
- package/{schema → dist/schema}/adapters.js +1 -2
- package/dist/schema/adapters.js.map +1 -0
- package/{schema → dist/schema}/help.d.ts +2 -0
- package/{schema → dist/schema}/help.js +6 -4
- package/dist/schema/help.js.map +1 -0
- package/{schema → dist/schema}/scale.js +1 -1
- package/dist/schema/scale.js.map +1 -0
- package/{schema → dist/schema}/sources.js +1 -1
- package/dist/schema/sources.js.map +1 -0
- package/{schema → dist/schema}/table.js +11 -10
- package/dist/schema/table.js.map +1 -0
- package/{schema → dist/schema}/types.d.ts +29 -1
- package/{schema → dist/schema}/types.js +1 -1
- package/dist/schema/types.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/table/table-view.d.ts +14 -0
- package/dist/table/table-view.js +140 -0
- package/dist/table/table-view.js.map +1 -0
- package/dist/table/table.css +73 -0
- package/dist/testing/corpus.d.ts +63 -0
- package/dist/testing/corpus.js +129 -0
- package/dist/testing/corpus.js.map +1 -0
- package/dist/testing/test-form.d.ts +11 -0
- package/dist/testing/test-form.js +38 -0
- package/dist/testing/test-form.js.map +1 -0
- package/dist/theme.css +345 -0
- package/dist/theme.d.ts +22 -0
- package/dist/theme.js +43 -0
- package/dist/theme.js.map +1 -0
- package/dist/tokens.css +542 -0
- package/dist/types.js +0 -0
- package/{ui → dist/ui}/action-tray.d.ts +1 -1
- package/{ui → dist/ui}/action-tray.js +20 -19
- package/dist/ui/action-tray.js.map +1 -0
- package/dist/ui/autocomplete.d.ts +30 -0
- package/dist/ui/autocomplete.js +139 -0
- package/dist/ui/autocomplete.js.map +1 -0
- package/{ui → dist/ui}/button.d.ts +3 -3
- package/{ui → dist/ui}/button.js +6 -7
- package/dist/ui/button.js.map +1 -0
- package/{ui → dist/ui}/choice-switch.js +1 -1
- package/dist/ui/choice-switch.js.map +1 -0
- package/{ui → dist/ui}/controls.js +51 -39
- package/dist/ui/controls.js.map +1 -0
- package/{ui → dist/ui}/display.d.ts +10 -3
- package/{ui → dist/ui}/display.js +57 -46
- package/dist/ui/display.js.map +1 -0
- package/{ui → dist/ui}/expanded-editor-modal.d.ts +2 -2
- package/{ui → dist/ui}/expanded-editor-modal.js +6 -6
- package/dist/ui/expanded-editor-modal.js.map +1 -0
- package/{ui → dist/ui}/field-context.js +1 -1
- package/dist/ui/field-context.js.map +1 -0
- package/{ui → dist/ui}/floating-panel.d.ts +1 -1
- package/{ui → dist/ui}/floating-panel.js +28 -12
- package/dist/ui/floating-panel.js.map +1 -0
- package/{ui → dist/ui}/index.d.ts +4 -0
- package/dist/ui/index.js +18 -0
- package/{ui → dist/ui}/input.d.ts +1 -1
- package/{ui → dist/ui}/input.js +9 -9
- package/dist/ui/input.js.map +1 -0
- package/{ui → dist/ui}/mode-bar.js +3 -3
- package/dist/ui/mode-bar.js.map +1 -0
- package/{ui → dist/ui}/overlay.d.ts +77 -5
- package/dist/ui/overlay.js +697 -0
- package/dist/ui/overlay.js.map +1 -0
- package/{ui → dist/ui}/path-crumbs.js +37 -3
- package/dist/ui/path-crumbs.js.map +1 -0
- package/{ui → dist/ui}/presence.js +2 -2
- package/dist/ui/presence.js.map +1 -0
- package/{ui → dist/ui}/select.js +89 -87
- package/dist/ui/select.js.map +1 -0
- package/{ui → dist/ui}/shortcut-hint.js +1 -1
- package/dist/ui/shortcut-hint.js.map +1 -0
- package/dist/ui/spinner.d.ts +24 -0
- package/dist/ui/spinner.js +42 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/ui/split-button.d.ts +32 -0
- package/dist/ui/split-button.js +60 -0
- package/dist/ui/split-button.js.map +1 -0
- package/{ui → dist/ui}/surfaces.js +1 -1
- package/dist/ui/surfaces.js.map +1 -0
- package/dist/ui/tab.d.ts +38 -0
- package/dist/ui/tab.js +87 -0
- package/dist/ui/tab.js.map +1 -0
- package/dist/ui/toast.d.ts +20 -0
- package/dist/ui/toast.js +115 -0
- package/dist/ui/toast.js.map +1 -0
- package/{ui → dist/ui}/tooltip-root.d.ts +4 -3
- package/{ui → dist/ui}/tooltip-root.js +9 -8
- package/dist/ui/tooltip-root.js.map +1 -0
- package/{ui → dist/ui}/variants.js +1 -1
- package/dist/ui/variants.js.map +1 -0
- package/package.json +77 -23
- package/adapters/icons.js.map +0 -1
- package/adapters/lossy.js +0 -233
- package/adapters/lossy.js.map +0 -1
- package/adapters/prod-types.js.map +0 -1
- package/adapters/schema.js.map +0 -1
- package/adapters/values.js.map +0 -1
- package/brand/logo.js +0 -9
- package/brand/logo.js.map +0 -1
- package/context.js.map +0 -1
- package/controls/button.js.map +0 -1
- package/controls/c1.js.map +0 -1
- package/controls/c2.js.map +0 -1
- package/controls/choice.js.map +0 -1
- package/controls/code-register.js.map +0 -1
- package/controls/code.js.map +0 -1
- package/controls/color.js.map +0 -1
- package/controls/date-register.js.map +0 -1
- package/controls/date-zone.js.map +0 -1
- package/controls/date.js.map +0 -1
- package/controls/form-picker-register.js.map +0 -1
- package/controls/form-picker.js.map +0 -1
- package/controls/icon-register.js.map +0 -1
- package/controls/icon.js.map +0 -1
- package/controls/index.js.map +0 -1
- package/controls/json-register.js.map +0 -1
- package/controls/json.js.map +0 -1
- package/controls/link.js.map +0 -1
- package/controls/location-register.js.map +0 -1
- package/controls/location.js.map +0 -1
- package/controls/media-register.js.map +0 -1
- package/controls/media.js.map +0 -1
- package/controls/number.js.map +0 -1
- package/controls/reference-register.js.map +0 -1
- package/controls/reference.js.map +0 -1
- package/controls/rich-text/blocks.d.ts +0 -28
- package/controls/rich-text/blocks.js.map +0 -1
- package/controls/rich-text/catalog.js.map +0 -1
- package/controls/rich-text/codec.js.map +0 -1
- package/controls/rich-text/component-card.js.map +0 -1
- package/controls/rich-text/control.js.map +0 -1
- package/controls/rich-text/dom.js.map +0 -1
- package/controls/rich-text/editor.js.map +0 -1
- package/controls/rich-text/floating.js.map +0 -1
- package/controls/rich-text/index.js.map +0 -1
- package/controls/rich-text/link-form.js +0 -111
- package/controls/rich-text/link-form.js.map +0 -1
- package/controls/rich-text/markdown.js.map +0 -1
- package/controls/rich-text/ops.js.map +0 -1
- package/controls/rich-text/toolbar.js.map +0 -1
- package/controls/scale.js.map +0 -1
- package/controls/slot-register.js.map +0 -1
- package/controls/slot-services.js +0 -38
- package/controls/slot-services.js.map +0 -1
- package/controls/slot.js.map +0 -1
- package/controls/table/index.js.map +0 -1
- package/controls/table/prod.js.map +0 -1
- package/controls/table/refs.js.map +0 -1
- package/controls/table/stored.js.map +0 -1
- package/controls/table/table-editor.js.map +0 -1
- package/controls/table/table-link.js.map +0 -1
- package/controls/table/table.js.map +0 -1
- package/controls/text.js.map +0 -1
- package/engine/array.js.map +0 -1
- package/engine/bits.js.map +0 -1
- package/engine/breadcrumb.js.map +0 -1
- package/engine/expand.js.map +0 -1
- package/engine/field-list.js.map +0 -1
- package/engine/field-shell.js.map +0 -1
- package/engine/frame.js.map +0 -1
- package/engine/group-summary.js.map +0 -1
- package/engine/grouping.js.map +0 -1
- package/engine/item-list.js.map +0 -1
- package/engine/kit-field.js.map +0 -1
- package/engine/schema-form.js.map +0 -1
- package/engine/slot.js.map +0 -1
- package/index.js +0 -55
- package/index.js.map +0 -1
- package/lib/cn.js.map +0 -1
- package/lib/columns.js.map +0 -1
- package/lib/frames.js.map +0 -1
- package/lib/icons.js.map +0 -1
- package/lib/path.js.map +0 -1
- package/lib/schema.js.map +0 -1
- package/lib/utils.js.map +0 -1
- package/lib/validate.js.map +0 -1
- package/schema/adapters.js.map +0 -1
- package/schema/help.js.map +0 -1
- package/schema/scale.js.map +0 -1
- package/schema/sources.js.map +0 -1
- package/schema/table.js.map +0 -1
- package/schema/types.js.map +0 -1
- package/ui/action-tray.js.map +0 -1
- package/ui/button.js.map +0 -1
- package/ui/choice-switch.js.map +0 -1
- package/ui/controls.js.map +0 -1
- package/ui/display.js.map +0 -1
- package/ui/expanded-editor-modal.js.map +0 -1
- package/ui/field-context.js.map +0 -1
- package/ui/floating-panel.js.map +0 -1
- package/ui/index.js +0 -13
- package/ui/input.js.map +0 -1
- package/ui/mode-bar.js.map +0 -1
- package/ui/overlay.js +0 -378
- package/ui/overlay.js.map +0 -1
- package/ui/path-crumbs.js.map +0 -1
- package/ui/presence.js.map +0 -1
- package/ui/select.js.map +0 -1
- package/ui/shortcut-hint.js.map +0 -1
- package/ui/surfaces.js.map +0 -1
- package/ui/tooltip-root.js.map +0 -1
- package/ui/variants.js.map +0 -1
- /package/{_virtual → dist/_virtual}/_rolldown/runtime.js +0 -0
- /package/{adapters → dist/adapters}/icons.d.ts +0 -0
- /package/{brand → dist/brand}/logo.d.ts +0 -0
- /package/{controls → dist/controls}/c1.d.ts +0 -0
- /package/{controls → dist/controls}/c2.d.ts +0 -0
- /package/{controls → dist/controls}/code-register.d.ts +0 -0
- /package/{controls → dist/controls}/code.d.ts +0 -0
- /package/{controls → dist/controls}/color.d.ts +0 -0
- /package/{controls → dist/controls}/date-register.d.ts +0 -0
- /package/{controls → dist/controls}/date-zone.d.ts +0 -0
- /package/{controls → dist/controls}/date.d.ts +0 -0
- /package/{controls → dist/controls}/form-picker-register.d.ts +0 -0
- /package/{controls → dist/controls}/form-picker.d.ts +0 -0
- /package/{controls → dist/controls}/icon-register.d.ts +0 -0
- /package/{controls → dist/controls}/json-register.d.ts +0 -0
- /package/{controls → dist/controls}/json.d.ts +0 -0
- /package/{controls → dist/controls}/link.d.ts +0 -0
- /package/{controls → dist/controls}/location-register.d.ts +0 -0
- /package/{controls → dist/controls}/location.d.ts +0 -0
- /package/{controls → dist/controls}/media-register.d.ts +0 -0
- /package/{controls → dist/controls}/number.d.ts +0 -0
- /package/{controls → dist/controls}/reference-register.d.ts +0 -0
- /package/{controls → dist/controls}/rich-text/index.d.ts +0 -0
- /package/{controls → dist/controls}/rich-text/link-form.d.ts +0 -0
- /package/{controls → dist/controls}/rich-text/markdown.d.ts +0 -0
- /package/{controls → dist/controls}/rich-text/ops.d.ts +0 -0
- /package/{controls → dist/controls}/scale.d.ts +0 -0
- /package/{controls → dist/controls}/slot-register.d.ts +0 -0
- /package/{controls → dist/controls}/slot.d.ts +0 -0
- /package/{controls → dist/controls}/table/index.d.ts +0 -0
- /package/{controls → dist/controls}/table/prod.d.ts +0 -0
- /package/{controls → dist/controls}/table/refs.d.ts +0 -0
- /package/{controls → dist/controls}/table/stored.d.ts +0 -0
- /package/{controls → dist/controls}/table/table-editor.d.ts +0 -0
- /package/{controls → dist/controls}/table/table-link.d.ts +0 -0
- /package/{controls → dist/controls}/table/table.d.ts +0 -0
- /package/{engine → dist/engine}/array.d.ts +0 -0
- /package/{engine → dist/engine}/bits.d.ts +0 -0
- /package/{engine → dist/engine}/field-list.d.ts +0 -0
- /package/{engine → dist/engine}/field-shell.d.ts +0 -0
- /package/{engine → dist/engine}/kit-field.d.ts +0 -0
- /package/{engine → dist/engine}/slot.d.ts +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{lib → dist/lib}/cn.d.ts +0 -0
- /package/{lib → dist/lib}/columns.d.ts +0 -0
- /package/{lib → dist/lib}/frames.d.ts +0 -0
- /package/{lib → dist/lib}/icons.d.ts +0 -0
- /package/{lib → dist/lib}/path.d.ts +0 -0
- /package/{lib → dist/lib}/utils.d.ts +0 -0
- /package/{schema → dist/schema}/adapters.d.ts +0 -0
- /package/{schema → dist/schema}/scale.d.ts +0 -0
- /package/{schema → dist/schema}/sources.d.ts +0 -0
- /package/{schema → dist/schema}/table.d.ts +0 -0
- /package/{types.d.ts → dist/types.d.ts} +0 -0
- /package/{ui → dist/ui}/choice-switch.d.ts +0 -0
- /package/{ui → dist/ui}/controls.d.ts +0 -0
- /package/{ui → dist/ui}/field-context.d.ts +0 -0
- /package/{ui → dist/ui}/mode-bar.d.ts +0 -0
- /package/{ui → dist/ui}/path-crumbs.d.ts +0 -0
- /package/{ui → dist/ui}/presence.d.ts +0 -0
- /package/{ui → dist/ui}/select.d.ts +0 -0
- /package/{ui → dist/ui}/shortcut-hint.d.ts +0 -0
- /package/{ui → dist/ui}/surfaces.d.ts +0 -0
- /package/{ui → dist/ui}/variants.d.ts +0 -0
|
@@ -0,0 +1,3157 @@
|
|
|
1
|
+
import { tableFrom } from "../../schema/table.js";
|
|
2
|
+
//#region src/library/entries/features.entries.ts
|
|
3
|
+
var img = (seed, alt, w = 1600, h = 1e3, focal) => ({
|
|
4
|
+
url: `https://picsum.photos/seed/${seed}/${w}/${h}`,
|
|
5
|
+
alt,
|
|
6
|
+
name: `${seed}.jpg`,
|
|
7
|
+
width: w * 1.5,
|
|
8
|
+
height: h * 1.5,
|
|
9
|
+
...focal ? { focal } : {}
|
|
10
|
+
});
|
|
11
|
+
var GROUPS = [
|
|
12
|
+
{
|
|
13
|
+
name: "content",
|
|
14
|
+
title: "Content",
|
|
15
|
+
default: true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "media",
|
|
19
|
+
title: "Media"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "design",
|
|
23
|
+
title: "Design"
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
var CONTENT_DESIGN = [{
|
|
27
|
+
name: "content",
|
|
28
|
+
title: "Content",
|
|
29
|
+
default: true
|
|
30
|
+
}, {
|
|
31
|
+
name: "design",
|
|
32
|
+
title: "Design"
|
|
33
|
+
}];
|
|
34
|
+
/** Section theme — the same three surfaces every block offers. Design, never content. */
|
|
35
|
+
var theme = {
|
|
36
|
+
name: "theme",
|
|
37
|
+
title: "Theme",
|
|
38
|
+
type: "select",
|
|
39
|
+
group: "design",
|
|
40
|
+
layout: "segmented",
|
|
41
|
+
initialValue: "light",
|
|
42
|
+
options: [
|
|
43
|
+
{
|
|
44
|
+
value: "light",
|
|
45
|
+
label: "Light"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: "dark",
|
|
49
|
+
label: "Dark"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
value: "brand",
|
|
53
|
+
label: "Brand"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
var anchor = {
|
|
58
|
+
name: "anchor",
|
|
59
|
+
title: "Anchor",
|
|
60
|
+
type: "string",
|
|
61
|
+
group: "design",
|
|
62
|
+
advanced: true,
|
|
63
|
+
validation: {
|
|
64
|
+
pattern: "^[a-z0-9-]*$",
|
|
65
|
+
patternMessage: "Use lowercase letters, numbers and hyphens, like our-story."
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var featureGrid = {
|
|
69
|
+
name: "featureGrid",
|
|
70
|
+
title: "Feature grid",
|
|
71
|
+
kind: "block",
|
|
72
|
+
icon: "layout-grid",
|
|
73
|
+
category: "Features & storytelling",
|
|
74
|
+
description: "Icon, title and a short line for each benefit, in 2–4 columns",
|
|
75
|
+
variants: [
|
|
76
|
+
{
|
|
77
|
+
id: "three",
|
|
78
|
+
name: "3 columns",
|
|
79
|
+
description: "Six or nine benefits read best"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "four",
|
|
83
|
+
name: "4 columns",
|
|
84
|
+
description: "Short titles, one-line text"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "list",
|
|
88
|
+
name: "Icon list",
|
|
89
|
+
description: "Two columns, icon beside text"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
preview: {
|
|
93
|
+
title: "heading",
|
|
94
|
+
subtitle: "eyebrow",
|
|
95
|
+
icon: "layout-grid"
|
|
96
|
+
},
|
|
97
|
+
groups: CONTENT_DESIGN,
|
|
98
|
+
fields: [
|
|
99
|
+
{
|
|
100
|
+
name: "eyebrow",
|
|
101
|
+
title: "Eyebrow",
|
|
102
|
+
type: "string",
|
|
103
|
+
validation: { max: 32 }
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "heading",
|
|
107
|
+
title: "Heading",
|
|
108
|
+
type: "string",
|
|
109
|
+
validation: {
|
|
110
|
+
required: true,
|
|
111
|
+
recommendedMax: 60
|
|
112
|
+
},
|
|
113
|
+
ai: true,
|
|
114
|
+
localized: true
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "intro",
|
|
118
|
+
title: "Intro",
|
|
119
|
+
type: "text",
|
|
120
|
+
rows: 2,
|
|
121
|
+
validation: { recommendedMax: 200 },
|
|
122
|
+
ai: true,
|
|
123
|
+
localized: true
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "features",
|
|
127
|
+
title: "Features",
|
|
128
|
+
type: "array",
|
|
129
|
+
itemNoun: "feature",
|
|
130
|
+
validation: {
|
|
131
|
+
min: 2,
|
|
132
|
+
max: 12
|
|
133
|
+
},
|
|
134
|
+
of: [{
|
|
135
|
+
name: "feature",
|
|
136
|
+
title: "Feature",
|
|
137
|
+
type: "object",
|
|
138
|
+
icon: "sparkles",
|
|
139
|
+
preview: {
|
|
140
|
+
title: "title",
|
|
141
|
+
subtitle: "body",
|
|
142
|
+
fallback: "Untitled feature"
|
|
143
|
+
},
|
|
144
|
+
fields: [
|
|
145
|
+
{
|
|
146
|
+
name: "icon",
|
|
147
|
+
title: "Icon",
|
|
148
|
+
type: "icon",
|
|
149
|
+
description: "Leave empty to show a number instead."
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "title",
|
|
153
|
+
title: "Title",
|
|
154
|
+
type: "string",
|
|
155
|
+
validation: {
|
|
156
|
+
required: true,
|
|
157
|
+
max: 48
|
|
158
|
+
},
|
|
159
|
+
ai: true,
|
|
160
|
+
localized: true
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "body",
|
|
164
|
+
title: "Body",
|
|
165
|
+
type: "text",
|
|
166
|
+
rows: 2,
|
|
167
|
+
validation: { recommendedMax: 140 },
|
|
168
|
+
ai: true,
|
|
169
|
+
localized: true
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "link",
|
|
173
|
+
title: "Learn more link",
|
|
174
|
+
type: "link"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "button",
|
|
181
|
+
title: "Button",
|
|
182
|
+
type: "button"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "iconStyle",
|
|
186
|
+
title: "Icon style",
|
|
187
|
+
type: "select",
|
|
188
|
+
group: "design",
|
|
189
|
+
layout: "cards",
|
|
190
|
+
initialValue: "tile",
|
|
191
|
+
options: [
|
|
192
|
+
{
|
|
193
|
+
value: "plain",
|
|
194
|
+
label: "Plain",
|
|
195
|
+
icon: "point",
|
|
196
|
+
description: "Icon only"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
value: "tile",
|
|
200
|
+
label: "Tile",
|
|
201
|
+
icon: "square",
|
|
202
|
+
description: "Icon on a soft square"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
value: "circle",
|
|
206
|
+
label: "Circle",
|
|
207
|
+
icon: "circle",
|
|
208
|
+
description: "Icon on a soft circle"
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "alignment",
|
|
214
|
+
title: "Alignment",
|
|
215
|
+
type: "select",
|
|
216
|
+
group: "design",
|
|
217
|
+
layout: "segmented",
|
|
218
|
+
initialValue: "left",
|
|
219
|
+
options: [{
|
|
220
|
+
value: "left",
|
|
221
|
+
label: "Left"
|
|
222
|
+
}, {
|
|
223
|
+
value: "center",
|
|
224
|
+
label: "Center"
|
|
225
|
+
}]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "showDividers",
|
|
229
|
+
title: "Show dividers",
|
|
230
|
+
type: "boolean",
|
|
231
|
+
group: "design"
|
|
232
|
+
},
|
|
233
|
+
theme,
|
|
234
|
+
anchor
|
|
235
|
+
]
|
|
236
|
+
};
|
|
237
|
+
var featureRows = {
|
|
238
|
+
name: "featureRows",
|
|
239
|
+
title: "Feature rows",
|
|
240
|
+
kind: "block",
|
|
241
|
+
icon: "layout-rows",
|
|
242
|
+
category: "Features & storytelling",
|
|
243
|
+
description: "Image and text side by side, swapping sides row after row",
|
|
244
|
+
variants: [{
|
|
245
|
+
id: "zigzag",
|
|
246
|
+
name: "Zigzag",
|
|
247
|
+
description: "Sides alternate automatically"
|
|
248
|
+
}, {
|
|
249
|
+
id: "sticky",
|
|
250
|
+
name: "Sticky media",
|
|
251
|
+
description: "Text scrolls past a pinned image"
|
|
252
|
+
}],
|
|
253
|
+
preview: {
|
|
254
|
+
title: "heading",
|
|
255
|
+
icon: "layout-rows"
|
|
256
|
+
},
|
|
257
|
+
groups: GROUPS,
|
|
258
|
+
fields: [
|
|
259
|
+
{
|
|
260
|
+
name: "heading",
|
|
261
|
+
title: "Heading",
|
|
262
|
+
type: "string",
|
|
263
|
+
validation: { recommendedMax: 60 },
|
|
264
|
+
ai: true,
|
|
265
|
+
localized: true
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "rows",
|
|
269
|
+
title: "Rows",
|
|
270
|
+
type: "array",
|
|
271
|
+
itemNoun: "row",
|
|
272
|
+
validation: {
|
|
273
|
+
min: 1,
|
|
274
|
+
max: 6
|
|
275
|
+
},
|
|
276
|
+
of: [{
|
|
277
|
+
name: "row",
|
|
278
|
+
title: "Row",
|
|
279
|
+
type: "object",
|
|
280
|
+
icon: "layout-rows",
|
|
281
|
+
preview: {
|
|
282
|
+
title: "title",
|
|
283
|
+
subtitle: "eyebrow",
|
|
284
|
+
media: "image",
|
|
285
|
+
fallback: "Untitled row"
|
|
286
|
+
},
|
|
287
|
+
fields: [
|
|
288
|
+
{
|
|
289
|
+
name: "eyebrow",
|
|
290
|
+
title: "Eyebrow",
|
|
291
|
+
type: "string",
|
|
292
|
+
validation: { max: 32 }
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: "title",
|
|
296
|
+
title: "Title",
|
|
297
|
+
type: "string",
|
|
298
|
+
validation: {
|
|
299
|
+
required: true,
|
|
300
|
+
recommendedMax: 60
|
|
301
|
+
},
|
|
302
|
+
ai: true,
|
|
303
|
+
localized: true
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: "body",
|
|
307
|
+
title: "Body",
|
|
308
|
+
type: "richtext",
|
|
309
|
+
ai: true,
|
|
310
|
+
localized: true
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "points",
|
|
314
|
+
title: "Points",
|
|
315
|
+
type: "array",
|
|
316
|
+
itemNoun: "point",
|
|
317
|
+
validation: { max: 5 },
|
|
318
|
+
of: [{
|
|
319
|
+
name: "point",
|
|
320
|
+
title: "Point",
|
|
321
|
+
type: "string",
|
|
322
|
+
validation: { max: 80 },
|
|
323
|
+
localized: true
|
|
324
|
+
}]
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "button",
|
|
328
|
+
title: "Button",
|
|
329
|
+
type: "button"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "mediaType",
|
|
333
|
+
title: "Media",
|
|
334
|
+
type: "select",
|
|
335
|
+
layout: "segmented",
|
|
336
|
+
initialValue: "image",
|
|
337
|
+
options: [{
|
|
338
|
+
value: "image",
|
|
339
|
+
label: "Image"
|
|
340
|
+
}, {
|
|
341
|
+
value: "video",
|
|
342
|
+
label: "Video"
|
|
343
|
+
}],
|
|
344
|
+
group: "media"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: "image",
|
|
348
|
+
title: "Image",
|
|
349
|
+
type: "image",
|
|
350
|
+
group: "media",
|
|
351
|
+
hidden: {
|
|
352
|
+
field: "mediaType",
|
|
353
|
+
equals: "image"
|
|
354
|
+
},
|
|
355
|
+
validation: { required: true }
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: "video",
|
|
359
|
+
title: "Video",
|
|
360
|
+
type: "video",
|
|
361
|
+
group: "media",
|
|
362
|
+
hidden: {
|
|
363
|
+
field: "mediaType",
|
|
364
|
+
equals: "video"
|
|
365
|
+
},
|
|
366
|
+
validation: { required: true },
|
|
367
|
+
description: "Plays muted and loops."
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "side",
|
|
371
|
+
title: "Image side",
|
|
372
|
+
type: "select",
|
|
373
|
+
group: "design",
|
|
374
|
+
layout: "segmented",
|
|
375
|
+
initialValue: "auto",
|
|
376
|
+
options: [
|
|
377
|
+
{
|
|
378
|
+
value: "auto",
|
|
379
|
+
label: "Auto"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
value: "left",
|
|
383
|
+
label: "Left"
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
value: "right",
|
|
387
|
+
label: "Right"
|
|
388
|
+
}
|
|
389
|
+
],
|
|
390
|
+
description: "Auto alternates from the section's first side."
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
}]
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "startSide",
|
|
397
|
+
title: "First image on",
|
|
398
|
+
type: "select",
|
|
399
|
+
group: "design",
|
|
400
|
+
layout: "segmented",
|
|
401
|
+
initialValue: "right",
|
|
402
|
+
options: [{
|
|
403
|
+
value: "left",
|
|
404
|
+
label: "Left"
|
|
405
|
+
}, {
|
|
406
|
+
value: "right",
|
|
407
|
+
label: "Right"
|
|
408
|
+
}],
|
|
409
|
+
description: "Later rows alternate from here."
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "mediaRatio",
|
|
413
|
+
title: "Media shape",
|
|
414
|
+
type: "select",
|
|
415
|
+
group: "design",
|
|
416
|
+
layout: "segmented",
|
|
417
|
+
initialValue: "4:3",
|
|
418
|
+
options: [
|
|
419
|
+
{
|
|
420
|
+
value: "1:1",
|
|
421
|
+
label: "Square"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
value: "4:3",
|
|
425
|
+
label: "4:3"
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
value: "3:4",
|
|
429
|
+
label: "Portrait"
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
theme,
|
|
434
|
+
anchor
|
|
435
|
+
]
|
|
436
|
+
};
|
|
437
|
+
var featureBento = {
|
|
438
|
+
name: "featureBento",
|
|
439
|
+
title: "Feature bento",
|
|
440
|
+
kind: "block",
|
|
441
|
+
icon: "layout-dashboard",
|
|
442
|
+
category: "Features & storytelling",
|
|
443
|
+
description: "Product capabilities as a mosaic of cells, each its own block, sized per screen",
|
|
444
|
+
variants: [{
|
|
445
|
+
id: "grid-4",
|
|
446
|
+
name: "4-column grid"
|
|
447
|
+
}, {
|
|
448
|
+
id: "grid-6",
|
|
449
|
+
name: "6-column grid",
|
|
450
|
+
description: "Finer spans for dense product tours"
|
|
451
|
+
}],
|
|
452
|
+
preview: {
|
|
453
|
+
title: "heading",
|
|
454
|
+
subtitle: "eyebrow",
|
|
455
|
+
icon: "layout-dashboard"
|
|
456
|
+
},
|
|
457
|
+
groups: CONTENT_DESIGN,
|
|
458
|
+
fields: [
|
|
459
|
+
{
|
|
460
|
+
name: "eyebrow",
|
|
461
|
+
title: "Eyebrow",
|
|
462
|
+
type: "string",
|
|
463
|
+
validation: { max: 32 }
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "heading",
|
|
467
|
+
title: "Heading",
|
|
468
|
+
type: "string",
|
|
469
|
+
validation: {
|
|
470
|
+
required: true,
|
|
471
|
+
recommendedMax: 60
|
|
472
|
+
},
|
|
473
|
+
ai: true,
|
|
474
|
+
localized: true
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "subheading",
|
|
478
|
+
title: "Subheading",
|
|
479
|
+
type: "text",
|
|
480
|
+
rows: 2,
|
|
481
|
+
validation: { recommendedMax: 180 },
|
|
482
|
+
ai: true,
|
|
483
|
+
localized: true
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "cells",
|
|
487
|
+
title: "Cells",
|
|
488
|
+
type: "slot",
|
|
489
|
+
validation: {
|
|
490
|
+
min: 3,
|
|
491
|
+
max: 10
|
|
492
|
+
},
|
|
493
|
+
of: {
|
|
494
|
+
type: "block",
|
|
495
|
+
allow: [
|
|
496
|
+
"featureBlock",
|
|
497
|
+
"statBlock",
|
|
498
|
+
"imageBlock",
|
|
499
|
+
"videoBlock",
|
|
500
|
+
"quoteBlock",
|
|
501
|
+
"ctaCard",
|
|
502
|
+
"textBlock"
|
|
503
|
+
]
|
|
504
|
+
},
|
|
505
|
+
slotSettings: [
|
|
506
|
+
{
|
|
507
|
+
name: "desktopWidth",
|
|
508
|
+
title: "Width on desktop",
|
|
509
|
+
type: "number",
|
|
510
|
+
layout: "slider",
|
|
511
|
+
initialValue: 2,
|
|
512
|
+
numeric: {
|
|
513
|
+
min: 1,
|
|
514
|
+
max: 6,
|
|
515
|
+
step: 1,
|
|
516
|
+
unit: "cols"
|
|
517
|
+
},
|
|
518
|
+
description: "In grid columns."
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
name: "rowsDesktop",
|
|
522
|
+
title: "Height on desktop",
|
|
523
|
+
type: "number",
|
|
524
|
+
layout: "slider",
|
|
525
|
+
initialValue: 1,
|
|
526
|
+
numeric: {
|
|
527
|
+
min: 1,
|
|
528
|
+
max: 3,
|
|
529
|
+
step: 1,
|
|
530
|
+
unit: "rows"
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "tabletWidth",
|
|
535
|
+
title: "Width on tablet",
|
|
536
|
+
type: "select",
|
|
537
|
+
layout: "segmented",
|
|
538
|
+
initialValue: "half",
|
|
539
|
+
options: [{
|
|
540
|
+
value: "half",
|
|
541
|
+
label: "Half"
|
|
542
|
+
}, {
|
|
543
|
+
value: "full",
|
|
544
|
+
label: "Full"
|
|
545
|
+
}]
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "mobileOrder",
|
|
549
|
+
title: "Order on phones",
|
|
550
|
+
type: "number",
|
|
551
|
+
numeric: {
|
|
552
|
+
min: 1,
|
|
553
|
+
max: 10,
|
|
554
|
+
step: 1
|
|
555
|
+
},
|
|
556
|
+
description: "Lower numbers go first. Leave empty to keep the desktop order."
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "surface",
|
|
560
|
+
title: "Surface",
|
|
561
|
+
type: "select",
|
|
562
|
+
layout: "segmented",
|
|
563
|
+
initialValue: "muted",
|
|
564
|
+
options: [
|
|
565
|
+
{
|
|
566
|
+
value: "muted",
|
|
567
|
+
label: "Muted"
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
value: "dark",
|
|
571
|
+
label: "Dark"
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
value: "brand",
|
|
575
|
+
label: "Brand"
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
value: "image",
|
|
579
|
+
label: "Image"
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
name: "background",
|
|
585
|
+
title: "Background image",
|
|
586
|
+
type: "image",
|
|
587
|
+
hidden: {
|
|
588
|
+
field: "surface",
|
|
589
|
+
equals: "image"
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
]
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "button",
|
|
596
|
+
title: "Button",
|
|
597
|
+
type: "button"
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
name: "gap",
|
|
601
|
+
title: "Gap",
|
|
602
|
+
type: "select",
|
|
603
|
+
group: "design",
|
|
604
|
+
layout: "segmented",
|
|
605
|
+
initialValue: "md",
|
|
606
|
+
options: [
|
|
607
|
+
{
|
|
608
|
+
value: "sm",
|
|
609
|
+
label: "S"
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
value: "md",
|
|
613
|
+
label: "M"
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
value: "lg",
|
|
617
|
+
label: "L"
|
|
618
|
+
}
|
|
619
|
+
]
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: "radius",
|
|
623
|
+
title: "Corner radius",
|
|
624
|
+
type: "select",
|
|
625
|
+
group: "design",
|
|
626
|
+
layout: "segmented",
|
|
627
|
+
initialValue: "lg",
|
|
628
|
+
options: [
|
|
629
|
+
{
|
|
630
|
+
value: "none",
|
|
631
|
+
label: "None"
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
value: "md",
|
|
635
|
+
label: "M"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
value: "lg",
|
|
639
|
+
label: "L"
|
|
640
|
+
}
|
|
641
|
+
]
|
|
642
|
+
},
|
|
643
|
+
theme
|
|
644
|
+
]
|
|
645
|
+
};
|
|
646
|
+
var steps = {
|
|
647
|
+
name: "howItWorks",
|
|
648
|
+
title: "Steps",
|
|
649
|
+
kind: "block",
|
|
650
|
+
icon: "list-numbers",
|
|
651
|
+
category: "Features & storytelling",
|
|
652
|
+
description: "A numbered process: how it works, getting started, what happens next",
|
|
653
|
+
variants: [
|
|
654
|
+
{
|
|
655
|
+
id: "row",
|
|
656
|
+
name: "In a row",
|
|
657
|
+
description: "Steps side by side with a connector line"
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
id: "stack",
|
|
661
|
+
name: "Vertical",
|
|
662
|
+
description: "A numbered column, room for images"
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
id: "cards",
|
|
666
|
+
name: "Cards"
|
|
667
|
+
}
|
|
668
|
+
],
|
|
669
|
+
preview: {
|
|
670
|
+
title: "heading",
|
|
671
|
+
icon: "list-numbers"
|
|
672
|
+
},
|
|
673
|
+
groups: CONTENT_DESIGN,
|
|
674
|
+
fields: [
|
|
675
|
+
{
|
|
676
|
+
name: "eyebrow",
|
|
677
|
+
title: "Eyebrow",
|
|
678
|
+
type: "string",
|
|
679
|
+
validation: { max: 32 }
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "heading",
|
|
683
|
+
title: "Heading",
|
|
684
|
+
type: "string",
|
|
685
|
+
validation: {
|
|
686
|
+
required: true,
|
|
687
|
+
recommendedMax: 60
|
|
688
|
+
},
|
|
689
|
+
ai: true,
|
|
690
|
+
localized: true
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
name: "steps",
|
|
694
|
+
title: "Steps",
|
|
695
|
+
type: "array",
|
|
696
|
+
itemNoun: "step",
|
|
697
|
+
validation: {
|
|
698
|
+
min: 2,
|
|
699
|
+
max: 6
|
|
700
|
+
},
|
|
701
|
+
description: "Numbers come from the order.",
|
|
702
|
+
of: [{
|
|
703
|
+
name: "step",
|
|
704
|
+
title: "Step",
|
|
705
|
+
type: "object",
|
|
706
|
+
icon: "circle-number-1",
|
|
707
|
+
preview: {
|
|
708
|
+
title: "title",
|
|
709
|
+
subtitle: "body",
|
|
710
|
+
media: "image",
|
|
711
|
+
fallback: "Untitled step"
|
|
712
|
+
},
|
|
713
|
+
fields: [
|
|
714
|
+
{
|
|
715
|
+
name: "title",
|
|
716
|
+
title: "Title",
|
|
717
|
+
type: "string",
|
|
718
|
+
validation: {
|
|
719
|
+
required: true,
|
|
720
|
+
max: 48
|
|
721
|
+
},
|
|
722
|
+
ai: true,
|
|
723
|
+
localized: true
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
name: "body",
|
|
727
|
+
title: "Body",
|
|
728
|
+
type: "text",
|
|
729
|
+
rows: 2,
|
|
730
|
+
validation: { recommendedMax: 160 },
|
|
731
|
+
ai: true,
|
|
732
|
+
localized: true
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
name: "icon",
|
|
736
|
+
title: "Icon",
|
|
737
|
+
type: "icon",
|
|
738
|
+
description: "Cards layout only. Replaces the number."
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
name: "duration",
|
|
742
|
+
title: "Time it takes",
|
|
743
|
+
type: "string",
|
|
744
|
+
placeholder: "2 minutes",
|
|
745
|
+
validation: { max: 20 }
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
name: "image",
|
|
749
|
+
title: "Image",
|
|
750
|
+
type: "image",
|
|
751
|
+
description: "Vertical layout only."
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: "link",
|
|
755
|
+
title: "Link",
|
|
756
|
+
type: "link"
|
|
757
|
+
}
|
|
758
|
+
]
|
|
759
|
+
}]
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
name: "button",
|
|
763
|
+
title: "Button",
|
|
764
|
+
type: "button"
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
name: "numberStyle",
|
|
768
|
+
title: "Numbering",
|
|
769
|
+
type: "select",
|
|
770
|
+
group: "design",
|
|
771
|
+
layout: "segmented",
|
|
772
|
+
initialValue: "digits",
|
|
773
|
+
options: [
|
|
774
|
+
{
|
|
775
|
+
value: "digits",
|
|
776
|
+
label: "1 2 3"
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
value: "padded",
|
|
780
|
+
label: "01 02 03"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
value: "none",
|
|
784
|
+
label: "None"
|
|
785
|
+
}
|
|
786
|
+
]
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
name: "showConnector",
|
|
790
|
+
title: "Connect the steps",
|
|
791
|
+
type: "boolean",
|
|
792
|
+
group: "design",
|
|
793
|
+
initialValue: true
|
|
794
|
+
},
|
|
795
|
+
theme
|
|
796
|
+
]
|
|
797
|
+
};
|
|
798
|
+
var timeline = {
|
|
799
|
+
name: "timeline",
|
|
800
|
+
title: "Timeline",
|
|
801
|
+
kind: "block",
|
|
802
|
+
icon: "timeline",
|
|
803
|
+
category: "Features & storytelling",
|
|
804
|
+
description: "Milestones in order: company history, product evolution, a trip log",
|
|
805
|
+
variants: [{
|
|
806
|
+
id: "vertical",
|
|
807
|
+
name: "Vertical",
|
|
808
|
+
description: "Alternating sides of a center line"
|
|
809
|
+
}, {
|
|
810
|
+
id: "horizontal",
|
|
811
|
+
name: "Horizontal scroll"
|
|
812
|
+
}],
|
|
813
|
+
preview: {
|
|
814
|
+
title: "heading",
|
|
815
|
+
icon: "timeline"
|
|
816
|
+
},
|
|
817
|
+
groups: GROUPS,
|
|
818
|
+
fields: [
|
|
819
|
+
{
|
|
820
|
+
name: "eyebrow",
|
|
821
|
+
title: "Eyebrow",
|
|
822
|
+
type: "string",
|
|
823
|
+
validation: { max: 32 }
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
name: "heading",
|
|
827
|
+
title: "Heading",
|
|
828
|
+
type: "string",
|
|
829
|
+
validation: {
|
|
830
|
+
required: true,
|
|
831
|
+
recommendedMax: 60
|
|
832
|
+
},
|
|
833
|
+
ai: true,
|
|
834
|
+
localized: true
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
name: "intro",
|
|
838
|
+
title: "Intro",
|
|
839
|
+
type: "text",
|
|
840
|
+
rows: 2,
|
|
841
|
+
localized: true
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: "milestones",
|
|
845
|
+
title: "Milestones",
|
|
846
|
+
type: "array",
|
|
847
|
+
itemNoun: "milestone",
|
|
848
|
+
validation: {
|
|
849
|
+
min: 2,
|
|
850
|
+
max: 30
|
|
851
|
+
},
|
|
852
|
+
description: "Shown in date order, whatever the order here.",
|
|
853
|
+
of: [{
|
|
854
|
+
name: "milestone",
|
|
855
|
+
title: "Milestone",
|
|
856
|
+
type: "object",
|
|
857
|
+
icon: "flag",
|
|
858
|
+
preview: {
|
|
859
|
+
title: "title",
|
|
860
|
+
subtitle: "year",
|
|
861
|
+
media: "image",
|
|
862
|
+
fallback: "Untitled milestone"
|
|
863
|
+
},
|
|
864
|
+
fields: [
|
|
865
|
+
{
|
|
866
|
+
name: "precision",
|
|
867
|
+
title: "Date shown as",
|
|
868
|
+
type: "select",
|
|
869
|
+
layout: "segmented",
|
|
870
|
+
initialValue: "year",
|
|
871
|
+
options: [
|
|
872
|
+
{
|
|
873
|
+
value: "year",
|
|
874
|
+
label: "Year"
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
value: "month",
|
|
878
|
+
label: "Month"
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
value: "day",
|
|
882
|
+
label: "Exact day"
|
|
883
|
+
}
|
|
884
|
+
]
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
name: "year",
|
|
888
|
+
title: "Year",
|
|
889
|
+
type: "number",
|
|
890
|
+
numeric: {
|
|
891
|
+
min: 1800,
|
|
892
|
+
max: 2100,
|
|
893
|
+
step: 1
|
|
894
|
+
},
|
|
895
|
+
hidden: {
|
|
896
|
+
field: "precision",
|
|
897
|
+
equals: "year"
|
|
898
|
+
},
|
|
899
|
+
validation: { required: true }
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
name: "date",
|
|
903
|
+
title: "Date",
|
|
904
|
+
type: "date",
|
|
905
|
+
hidden: {
|
|
906
|
+
field: "precision",
|
|
907
|
+
equals: ["month", "day"]
|
|
908
|
+
},
|
|
909
|
+
validation: { required: true },
|
|
910
|
+
description: "For Month, any day in the month works."
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
name: "label",
|
|
914
|
+
title: "Label override",
|
|
915
|
+
type: "string",
|
|
916
|
+
placeholder: "Spring 2016",
|
|
917
|
+
validation: { max: 24 },
|
|
918
|
+
advanced: true,
|
|
919
|
+
description: "Replaces the formatted date. Sorting still uses the date."
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
name: "title",
|
|
923
|
+
title: "Title",
|
|
924
|
+
type: "string",
|
|
925
|
+
validation: {
|
|
926
|
+
required: true,
|
|
927
|
+
max: 64
|
|
928
|
+
},
|
|
929
|
+
ai: true,
|
|
930
|
+
localized: true
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
name: "body",
|
|
934
|
+
title: "Body",
|
|
935
|
+
type: "richtext",
|
|
936
|
+
localized: true
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
name: "image",
|
|
940
|
+
title: "Image",
|
|
941
|
+
type: "image",
|
|
942
|
+
group: "media"
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: "isHighlighted",
|
|
946
|
+
title: "Highlight this milestone",
|
|
947
|
+
type: "boolean"
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
}]
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
name: "order",
|
|
954
|
+
title: "Order",
|
|
955
|
+
type: "select",
|
|
956
|
+
group: "design",
|
|
957
|
+
layout: "segmented",
|
|
958
|
+
initialValue: "asc",
|
|
959
|
+
options: [{
|
|
960
|
+
value: "asc",
|
|
961
|
+
label: "Oldest first"
|
|
962
|
+
}, {
|
|
963
|
+
value: "desc",
|
|
964
|
+
label: "Newest first"
|
|
965
|
+
}]
|
|
966
|
+
},
|
|
967
|
+
theme,
|
|
968
|
+
anchor
|
|
969
|
+
]
|
|
970
|
+
};
|
|
971
|
+
var mediaTabs = {
|
|
972
|
+
name: "mediaTabs",
|
|
973
|
+
title: "Tabs with media",
|
|
974
|
+
kind: "block",
|
|
975
|
+
icon: "browser",
|
|
976
|
+
category: "Features & storytelling",
|
|
977
|
+
description: "A fixed-shape product tour: each tab has copy and its own screenshot or clip",
|
|
978
|
+
variants: [{
|
|
979
|
+
id: "top",
|
|
980
|
+
name: "Tabs on top"
|
|
981
|
+
}, {
|
|
982
|
+
id: "side",
|
|
983
|
+
name: "Tabs on the side",
|
|
984
|
+
description: "Tab labels stack with their descriptions"
|
|
985
|
+
}],
|
|
986
|
+
preview: {
|
|
987
|
+
title: "heading",
|
|
988
|
+
icon: "layout-navbar"
|
|
989
|
+
},
|
|
990
|
+
groups: GROUPS,
|
|
991
|
+
fields: [
|
|
992
|
+
{
|
|
993
|
+
name: "eyebrow",
|
|
994
|
+
title: "Eyebrow",
|
|
995
|
+
type: "string",
|
|
996
|
+
validation: { max: 32 }
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
name: "heading",
|
|
1000
|
+
title: "Heading",
|
|
1001
|
+
type: "string",
|
|
1002
|
+
validation: {
|
|
1003
|
+
required: true,
|
|
1004
|
+
recommendedMax: 60
|
|
1005
|
+
},
|
|
1006
|
+
ai: true,
|
|
1007
|
+
localized: true
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
name: "tabs",
|
|
1011
|
+
title: "Tabs",
|
|
1012
|
+
type: "array",
|
|
1013
|
+
itemNoun: "tab",
|
|
1014
|
+
validation: {
|
|
1015
|
+
min: 2,
|
|
1016
|
+
max: 6
|
|
1017
|
+
},
|
|
1018
|
+
of: [{
|
|
1019
|
+
name: "tab",
|
|
1020
|
+
title: "Tab",
|
|
1021
|
+
type: "object",
|
|
1022
|
+
icon: "layout-navbar",
|
|
1023
|
+
preview: {
|
|
1024
|
+
title: "label",
|
|
1025
|
+
subtitle: "title",
|
|
1026
|
+
media: "screenshot",
|
|
1027
|
+
fallback: "Untitled tab"
|
|
1028
|
+
},
|
|
1029
|
+
fields: [
|
|
1030
|
+
{
|
|
1031
|
+
name: "label",
|
|
1032
|
+
title: "Tab label",
|
|
1033
|
+
type: "string",
|
|
1034
|
+
validation: {
|
|
1035
|
+
required: true,
|
|
1036
|
+
max: 20
|
|
1037
|
+
},
|
|
1038
|
+
localized: true
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
name: "icon",
|
|
1042
|
+
title: "Icon",
|
|
1043
|
+
type: "icon"
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
name: "summary",
|
|
1047
|
+
title: "Tab description",
|
|
1048
|
+
type: "text",
|
|
1049
|
+
rows: 2,
|
|
1050
|
+
validation: { recommendedMax: 90 },
|
|
1051
|
+
localized: true,
|
|
1052
|
+
description: "Side layout only."
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
name: "title",
|
|
1056
|
+
title: "Panel title",
|
|
1057
|
+
type: "string",
|
|
1058
|
+
validation: { recommendedMax: 60 },
|
|
1059
|
+
ai: true,
|
|
1060
|
+
localized: true
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
name: "body",
|
|
1064
|
+
title: "Body",
|
|
1065
|
+
type: "richtext",
|
|
1066
|
+
ai: true,
|
|
1067
|
+
localized: true
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
name: "button",
|
|
1071
|
+
title: "Button",
|
|
1072
|
+
type: "button"
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
name: "mediaType",
|
|
1076
|
+
title: "Media",
|
|
1077
|
+
type: "select",
|
|
1078
|
+
layout: "segmented",
|
|
1079
|
+
group: "media",
|
|
1080
|
+
initialValue: "image",
|
|
1081
|
+
options: [{
|
|
1082
|
+
value: "image",
|
|
1083
|
+
label: "Image"
|
|
1084
|
+
}, {
|
|
1085
|
+
value: "video",
|
|
1086
|
+
label: "Video"
|
|
1087
|
+
}]
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
name: "screenshot",
|
|
1091
|
+
title: "Screenshot",
|
|
1092
|
+
type: "image",
|
|
1093
|
+
group: "media",
|
|
1094
|
+
hidden: {
|
|
1095
|
+
field: "mediaType",
|
|
1096
|
+
equals: "image"
|
|
1097
|
+
},
|
|
1098
|
+
validation: { required: true },
|
|
1099
|
+
description: "Export at 2× the shown size."
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
name: "video",
|
|
1103
|
+
title: "Clip",
|
|
1104
|
+
type: "video",
|
|
1105
|
+
group: "media",
|
|
1106
|
+
hidden: {
|
|
1107
|
+
field: "mediaType",
|
|
1108
|
+
equals: "video"
|
|
1109
|
+
},
|
|
1110
|
+
validation: { required: true }
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "poster",
|
|
1114
|
+
title: "Poster",
|
|
1115
|
+
type: "image",
|
|
1116
|
+
group: "media",
|
|
1117
|
+
hidden: {
|
|
1118
|
+
field: "mediaType",
|
|
1119
|
+
equals: "video"
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
]
|
|
1123
|
+
}]
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
name: "autoplay",
|
|
1127
|
+
title: "Play automatically",
|
|
1128
|
+
type: "boolean",
|
|
1129
|
+
group: "design"
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
name: "interval",
|
|
1133
|
+
title: "Seconds per tab",
|
|
1134
|
+
type: "number",
|
|
1135
|
+
group: "design",
|
|
1136
|
+
layout: "slider",
|
|
1137
|
+
hidden: {
|
|
1138
|
+
field: "autoplay",
|
|
1139
|
+
equals: true
|
|
1140
|
+
},
|
|
1141
|
+
initialValue: 6,
|
|
1142
|
+
numeric: {
|
|
1143
|
+
min: 3,
|
|
1144
|
+
max: 15,
|
|
1145
|
+
step: 1,
|
|
1146
|
+
unit: "s"
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
name: "frame",
|
|
1151
|
+
title: "Media frame",
|
|
1152
|
+
type: "select",
|
|
1153
|
+
group: "design",
|
|
1154
|
+
layout: "segmented",
|
|
1155
|
+
initialValue: "browser",
|
|
1156
|
+
options: [
|
|
1157
|
+
{
|
|
1158
|
+
value: "none",
|
|
1159
|
+
label: "None"
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
value: "browser",
|
|
1163
|
+
label: "Browser"
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
value: "device",
|
|
1167
|
+
label: "Phone"
|
|
1168
|
+
}
|
|
1169
|
+
]
|
|
1170
|
+
},
|
|
1171
|
+
theme
|
|
1172
|
+
]
|
|
1173
|
+
};
|
|
1174
|
+
var accordionFeatures = {
|
|
1175
|
+
name: "accordionFeatures",
|
|
1176
|
+
title: "Accordion features",
|
|
1177
|
+
kind: "block",
|
|
1178
|
+
icon: "list-details",
|
|
1179
|
+
category: "Features & storytelling",
|
|
1180
|
+
description: "An accordion of features; opening one swaps the image beside it",
|
|
1181
|
+
variants: [{
|
|
1182
|
+
id: "image-right",
|
|
1183
|
+
name: "Image right"
|
|
1184
|
+
}, {
|
|
1185
|
+
id: "image-left",
|
|
1186
|
+
name: "Image left"
|
|
1187
|
+
}],
|
|
1188
|
+
preview: {
|
|
1189
|
+
title: "heading",
|
|
1190
|
+
icon: "layout-list"
|
|
1191
|
+
},
|
|
1192
|
+
groups: GROUPS,
|
|
1193
|
+
fields: [
|
|
1194
|
+
{
|
|
1195
|
+
name: "eyebrow",
|
|
1196
|
+
title: "Eyebrow",
|
|
1197
|
+
type: "string",
|
|
1198
|
+
validation: { max: 32 }
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
name: "heading",
|
|
1202
|
+
title: "Heading",
|
|
1203
|
+
type: "string",
|
|
1204
|
+
validation: {
|
|
1205
|
+
required: true,
|
|
1206
|
+
recommendedMax: 60
|
|
1207
|
+
},
|
|
1208
|
+
ai: true,
|
|
1209
|
+
localized: true
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
name: "items",
|
|
1213
|
+
title: "Features",
|
|
1214
|
+
type: "array",
|
|
1215
|
+
itemNoun: "feature",
|
|
1216
|
+
validation: {
|
|
1217
|
+
min: 2,
|
|
1218
|
+
max: 7
|
|
1219
|
+
},
|
|
1220
|
+
of: [{
|
|
1221
|
+
name: "item",
|
|
1222
|
+
title: "Feature",
|
|
1223
|
+
type: "object",
|
|
1224
|
+
icon: "chevron-down",
|
|
1225
|
+
preview: {
|
|
1226
|
+
title: "title",
|
|
1227
|
+
media: "image",
|
|
1228
|
+
fallback: "Untitled feature"
|
|
1229
|
+
},
|
|
1230
|
+
fields: [
|
|
1231
|
+
{
|
|
1232
|
+
name: "title",
|
|
1233
|
+
title: "Title",
|
|
1234
|
+
type: "string",
|
|
1235
|
+
validation: {
|
|
1236
|
+
required: true,
|
|
1237
|
+
max: 56
|
|
1238
|
+
},
|
|
1239
|
+
ai: true,
|
|
1240
|
+
localized: true
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
name: "body",
|
|
1244
|
+
title: "Body",
|
|
1245
|
+
type: "richtext",
|
|
1246
|
+
ai: true,
|
|
1247
|
+
localized: true
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
name: "image",
|
|
1251
|
+
title: "Image",
|
|
1252
|
+
type: "image",
|
|
1253
|
+
group: "media",
|
|
1254
|
+
validation: { required: true },
|
|
1255
|
+
description: "Replaces the main image while this row is open."
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
name: "link",
|
|
1259
|
+
title: "Link",
|
|
1260
|
+
type: "link"
|
|
1261
|
+
}
|
|
1262
|
+
]
|
|
1263
|
+
}]
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
name: "isFirstOpen",
|
|
1267
|
+
title: "Open the first feature",
|
|
1268
|
+
type: "boolean",
|
|
1269
|
+
group: "design",
|
|
1270
|
+
initialValue: true
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
name: "autoplay",
|
|
1274
|
+
title: "Play automatically",
|
|
1275
|
+
type: "boolean",
|
|
1276
|
+
group: "design"
|
|
1277
|
+
},
|
|
1278
|
+
theme
|
|
1279
|
+
]
|
|
1280
|
+
};
|
|
1281
|
+
var comparisonTable = {
|
|
1282
|
+
name: "competitorComparison",
|
|
1283
|
+
title: "Competitor comparison",
|
|
1284
|
+
kind: "block",
|
|
1285
|
+
icon: "vs",
|
|
1286
|
+
category: "Features & storytelling",
|
|
1287
|
+
description: "Your product against alternatives, row by row: included, not included, or a few words",
|
|
1288
|
+
variants: [{
|
|
1289
|
+
id: "table",
|
|
1290
|
+
name: "Table"
|
|
1291
|
+
}, {
|
|
1292
|
+
id: "cards",
|
|
1293
|
+
name: "Cards on phones",
|
|
1294
|
+
description: "Each competitor becomes a card below 640px"
|
|
1295
|
+
}],
|
|
1296
|
+
preview: {
|
|
1297
|
+
title: "heading",
|
|
1298
|
+
icon: "table"
|
|
1299
|
+
},
|
|
1300
|
+
groups: [
|
|
1301
|
+
{
|
|
1302
|
+
name: "content",
|
|
1303
|
+
title: "Content",
|
|
1304
|
+
default: true
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: "columns",
|
|
1308
|
+
title: "Brands"
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
name: "design",
|
|
1312
|
+
title: "Design"
|
|
1313
|
+
}
|
|
1314
|
+
],
|
|
1315
|
+
fields: [
|
|
1316
|
+
{
|
|
1317
|
+
name: "eyebrow",
|
|
1318
|
+
title: "Eyebrow",
|
|
1319
|
+
type: "string",
|
|
1320
|
+
validation: { max: 32 }
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
name: "heading",
|
|
1324
|
+
title: "Heading",
|
|
1325
|
+
type: "string",
|
|
1326
|
+
validation: {
|
|
1327
|
+
required: true,
|
|
1328
|
+
recommendedMax: 60
|
|
1329
|
+
},
|
|
1330
|
+
ai: true,
|
|
1331
|
+
localized: true
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
name: "intro",
|
|
1335
|
+
title: "Intro",
|
|
1336
|
+
type: "text",
|
|
1337
|
+
rows: 2,
|
|
1338
|
+
localized: true
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
name: "columns",
|
|
1342
|
+
title: "Columns",
|
|
1343
|
+
type: "array",
|
|
1344
|
+
group: "columns",
|
|
1345
|
+
itemNoun: "column",
|
|
1346
|
+
validation: {
|
|
1347
|
+
min: 2,
|
|
1348
|
+
max: 5
|
|
1349
|
+
},
|
|
1350
|
+
description: "The first column is you.",
|
|
1351
|
+
of: [{
|
|
1352
|
+
name: "column",
|
|
1353
|
+
title: "Column",
|
|
1354
|
+
type: "object",
|
|
1355
|
+
icon: "columns",
|
|
1356
|
+
preview: {
|
|
1357
|
+
title: "name",
|
|
1358
|
+
subtitle: "key",
|
|
1359
|
+
media: "logo",
|
|
1360
|
+
fallback: "Untitled column"
|
|
1361
|
+
},
|
|
1362
|
+
fields: [
|
|
1363
|
+
{
|
|
1364
|
+
name: "key",
|
|
1365
|
+
title: "Key",
|
|
1366
|
+
type: "slug",
|
|
1367
|
+
access: { edit: "schema.manage" },
|
|
1368
|
+
lockReason: "The table's column uses this key. Changing it would orphan its cells.",
|
|
1369
|
+
validation: {
|
|
1370
|
+
required: true,
|
|
1371
|
+
unique: true
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
name: "name",
|
|
1376
|
+
title: "Name",
|
|
1377
|
+
type: "string",
|
|
1378
|
+
validation: {
|
|
1379
|
+
required: true,
|
|
1380
|
+
max: 24
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
name: "logo",
|
|
1385
|
+
title: "Logo",
|
|
1386
|
+
type: "image",
|
|
1387
|
+
description: "SVG or a transparent PNG."
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
name: "isHighlighted",
|
|
1391
|
+
title: "Highlight this column",
|
|
1392
|
+
type: "boolean"
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
name: "badge",
|
|
1396
|
+
title: "Badge",
|
|
1397
|
+
type: "string",
|
|
1398
|
+
hidden: {
|
|
1399
|
+
field: "isHighlighted",
|
|
1400
|
+
equals: true
|
|
1401
|
+
},
|
|
1402
|
+
placeholder: "Best value",
|
|
1403
|
+
validation: { max: 16 }
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
name: "price",
|
|
1407
|
+
title: "Price line",
|
|
1408
|
+
type: "string",
|
|
1409
|
+
placeholder: "$29 / month",
|
|
1410
|
+
validation: { max: 24 }
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
name: "button",
|
|
1414
|
+
title: "Button",
|
|
1415
|
+
type: "button",
|
|
1416
|
+
hidden: {
|
|
1417
|
+
field: "isHighlighted",
|
|
1418
|
+
equals: true
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
]
|
|
1422
|
+
}]
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
name: "comparison",
|
|
1426
|
+
title: "Comparison",
|
|
1427
|
+
type: "table",
|
|
1428
|
+
localized: true,
|
|
1429
|
+
validation: { required: true },
|
|
1430
|
+
table: {
|
|
1431
|
+
maxColumns: 6,
|
|
1432
|
+
maxRows: 60,
|
|
1433
|
+
columnTypes: [
|
|
1434
|
+
"text",
|
|
1435
|
+
"boolean",
|
|
1436
|
+
"number",
|
|
1437
|
+
"money",
|
|
1438
|
+
"rating",
|
|
1439
|
+
"link"
|
|
1440
|
+
],
|
|
1441
|
+
jsonLd: ["ItemList"],
|
|
1442
|
+
settings: {
|
|
1443
|
+
totals: false,
|
|
1444
|
+
phones: []
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
name: "verifiedOn",
|
|
1450
|
+
title: "Last checked",
|
|
1451
|
+
type: "date",
|
|
1452
|
+
description: "Shown under the table.",
|
|
1453
|
+
validation: { required: true }
|
|
1454
|
+
},
|
|
1455
|
+
theme
|
|
1456
|
+
]
|
|
1457
|
+
};
|
|
1458
|
+
var statsBand = {
|
|
1459
|
+
name: "statsBand",
|
|
1460
|
+
title: "Stats band",
|
|
1461
|
+
kind: "block",
|
|
1462
|
+
icon: "chart-bar",
|
|
1463
|
+
category: "Features & storytelling",
|
|
1464
|
+
description: "Three to five big numbers in a row, each with its source",
|
|
1465
|
+
variants: [{
|
|
1466
|
+
id: "inline",
|
|
1467
|
+
name: "Inline",
|
|
1468
|
+
description: "Numbers in one row, dividers between"
|
|
1469
|
+
}, {
|
|
1470
|
+
id: "split",
|
|
1471
|
+
name: "Heading + stats",
|
|
1472
|
+
description: "Heading left, numbers right"
|
|
1473
|
+
}],
|
|
1474
|
+
preview: {
|
|
1475
|
+
title: "heading",
|
|
1476
|
+
icon: "chart-bar"
|
|
1477
|
+
},
|
|
1478
|
+
groups: CONTENT_DESIGN,
|
|
1479
|
+
fields: [
|
|
1480
|
+
{
|
|
1481
|
+
name: "heading",
|
|
1482
|
+
title: "Heading",
|
|
1483
|
+
type: "string",
|
|
1484
|
+
validation: { recommendedMax: 60 },
|
|
1485
|
+
ai: true,
|
|
1486
|
+
localized: true,
|
|
1487
|
+
description: "Heading + stats layout only."
|
|
1488
|
+
},
|
|
1489
|
+
{
|
|
1490
|
+
name: "stats",
|
|
1491
|
+
title: "Stats",
|
|
1492
|
+
type: "array",
|
|
1493
|
+
itemNoun: "stat",
|
|
1494
|
+
validation: {
|
|
1495
|
+
min: 2,
|
|
1496
|
+
max: 5
|
|
1497
|
+
},
|
|
1498
|
+
of: [{
|
|
1499
|
+
name: "stat",
|
|
1500
|
+
title: "Stat",
|
|
1501
|
+
type: "object",
|
|
1502
|
+
icon: "number",
|
|
1503
|
+
preview: {
|
|
1504
|
+
title: "label",
|
|
1505
|
+
subtitle: "value",
|
|
1506
|
+
fallback: "Untitled stat"
|
|
1507
|
+
},
|
|
1508
|
+
fields: [
|
|
1509
|
+
{
|
|
1510
|
+
name: "prefix",
|
|
1511
|
+
title: "Before",
|
|
1512
|
+
type: "string",
|
|
1513
|
+
placeholder: "$",
|
|
1514
|
+
validation: { max: 3 },
|
|
1515
|
+
description: "Shown before the number, e.g. $."
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
name: "value",
|
|
1519
|
+
title: "Number",
|
|
1520
|
+
type: "number",
|
|
1521
|
+
validation: { required: true },
|
|
1522
|
+
numeric: { step: .1 },
|
|
1523
|
+
description: "Digits only. Formatting is added per language."
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
name: "suffix",
|
|
1527
|
+
title: "After",
|
|
1528
|
+
type: "string",
|
|
1529
|
+
placeholder: "k+",
|
|
1530
|
+
validation: { max: 6 },
|
|
1531
|
+
description: "Shown after the number, e.g. %, k+, yrs."
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
name: "decimals",
|
|
1535
|
+
title: "Decimals",
|
|
1536
|
+
type: "number",
|
|
1537
|
+
numeric: {
|
|
1538
|
+
min: 0,
|
|
1539
|
+
max: 2,
|
|
1540
|
+
step: 1
|
|
1541
|
+
},
|
|
1542
|
+
initialValue: 0,
|
|
1543
|
+
advanced: true
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
name: "label",
|
|
1547
|
+
title: "Label",
|
|
1548
|
+
type: "string",
|
|
1549
|
+
validation: {
|
|
1550
|
+
required: true,
|
|
1551
|
+
max: 48
|
|
1552
|
+
},
|
|
1553
|
+
localized: true
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
name: "source",
|
|
1557
|
+
title: "Source",
|
|
1558
|
+
type: "link"
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
name: "asOf",
|
|
1562
|
+
title: "As of",
|
|
1563
|
+
type: "date"
|
|
1564
|
+
}
|
|
1565
|
+
]
|
|
1566
|
+
}]
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
name: "useCountUp",
|
|
1570
|
+
title: "Count up on scroll",
|
|
1571
|
+
type: "boolean",
|
|
1572
|
+
group: "design",
|
|
1573
|
+
initialValue: true
|
|
1574
|
+
},
|
|
1575
|
+
theme
|
|
1576
|
+
]
|
|
1577
|
+
};
|
|
1578
|
+
var manifesto = {
|
|
1579
|
+
name: "manifesto",
|
|
1580
|
+
title: "Manifesto",
|
|
1581
|
+
kind: "block",
|
|
1582
|
+
icon: "blockquote",
|
|
1583
|
+
category: "Features & storytelling",
|
|
1584
|
+
description: "A big-type statement of belief or a single pull quote, with attribution",
|
|
1585
|
+
variants: [{
|
|
1586
|
+
id: "lines",
|
|
1587
|
+
name: "Manifesto lines",
|
|
1588
|
+
description: "One belief per line, revealed on scroll"
|
|
1589
|
+
}, {
|
|
1590
|
+
id: "quote",
|
|
1591
|
+
name: "Pull quote",
|
|
1592
|
+
description: "One large quote with a signature"
|
|
1593
|
+
}],
|
|
1594
|
+
preview: {
|
|
1595
|
+
title: "eyebrow",
|
|
1596
|
+
subtitle: "attribution.name",
|
|
1597
|
+
icon: "quote"
|
|
1598
|
+
},
|
|
1599
|
+
groups: GROUPS,
|
|
1600
|
+
fields: [
|
|
1601
|
+
{
|
|
1602
|
+
name: "eyebrow",
|
|
1603
|
+
title: "Eyebrow",
|
|
1604
|
+
type: "string",
|
|
1605
|
+
validation: { max: 32 }
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
name: "mode",
|
|
1609
|
+
title: "Content",
|
|
1610
|
+
type: "select",
|
|
1611
|
+
layout: "segmented",
|
|
1612
|
+
initialValue: "lines",
|
|
1613
|
+
options: [{
|
|
1614
|
+
value: "lines",
|
|
1615
|
+
label: "Lines"
|
|
1616
|
+
}, {
|
|
1617
|
+
value: "quote",
|
|
1618
|
+
label: "Quote"
|
|
1619
|
+
}]
|
|
1620
|
+
},
|
|
1621
|
+
{
|
|
1622
|
+
name: "lines",
|
|
1623
|
+
title: "Lines",
|
|
1624
|
+
type: "array",
|
|
1625
|
+
itemNoun: "line",
|
|
1626
|
+
hidden: {
|
|
1627
|
+
field: "mode",
|
|
1628
|
+
equals: "lines"
|
|
1629
|
+
},
|
|
1630
|
+
validation: {
|
|
1631
|
+
min: 2,
|
|
1632
|
+
max: 8
|
|
1633
|
+
},
|
|
1634
|
+
of: [{
|
|
1635
|
+
name: "line",
|
|
1636
|
+
title: "Line",
|
|
1637
|
+
type: "richtext",
|
|
1638
|
+
localized: true,
|
|
1639
|
+
description: "Italic words are emphasised."
|
|
1640
|
+
}]
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
name: "quote",
|
|
1644
|
+
title: "Quote",
|
|
1645
|
+
type: "text",
|
|
1646
|
+
rows: 4,
|
|
1647
|
+
hidden: {
|
|
1648
|
+
field: "mode",
|
|
1649
|
+
equals: "quote"
|
|
1650
|
+
},
|
|
1651
|
+
validation: {
|
|
1652
|
+
required: true,
|
|
1653
|
+
recommendedMax: 220
|
|
1654
|
+
},
|
|
1655
|
+
localized: true,
|
|
1656
|
+
description: "Without quotation marks; the design adds them."
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
name: "attribution",
|
|
1660
|
+
title: "Attribution",
|
|
1661
|
+
type: "object",
|
|
1662
|
+
icon: "signature",
|
|
1663
|
+
openMode: "inline",
|
|
1664
|
+
fields: [
|
|
1665
|
+
{
|
|
1666
|
+
name: "name",
|
|
1667
|
+
title: "Name",
|
|
1668
|
+
type: "string",
|
|
1669
|
+
validation: { max: 48 }
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
name: "role",
|
|
1673
|
+
title: "Role",
|
|
1674
|
+
type: "string",
|
|
1675
|
+
validation: { max: 64 },
|
|
1676
|
+
localized: true
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
name: "signature",
|
|
1680
|
+
title: "Signature",
|
|
1681
|
+
type: "image",
|
|
1682
|
+
description: "A transparent PNG or SVG."
|
|
1683
|
+
}
|
|
1684
|
+
]
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
name: "background",
|
|
1688
|
+
title: "Background image",
|
|
1689
|
+
type: "image",
|
|
1690
|
+
group: "media"
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
name: "overlay",
|
|
1694
|
+
title: "Image overlay",
|
|
1695
|
+
type: "number",
|
|
1696
|
+
group: "design",
|
|
1697
|
+
layout: "slider",
|
|
1698
|
+
hidden: {
|
|
1699
|
+
field: "background",
|
|
1700
|
+
notEmpty: true
|
|
1701
|
+
},
|
|
1702
|
+
initialValue: 45,
|
|
1703
|
+
numeric: {
|
|
1704
|
+
min: 0,
|
|
1705
|
+
max: 80,
|
|
1706
|
+
step: 5,
|
|
1707
|
+
unit: "%"
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
name: "size",
|
|
1712
|
+
title: "Type size",
|
|
1713
|
+
type: "select",
|
|
1714
|
+
group: "design",
|
|
1715
|
+
layout: "segmented",
|
|
1716
|
+
initialValue: "xl",
|
|
1717
|
+
options: [
|
|
1718
|
+
{
|
|
1719
|
+
value: "lg",
|
|
1720
|
+
label: "Large"
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
value: "xl",
|
|
1724
|
+
label: "Extra large"
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
value: "display",
|
|
1728
|
+
label: "Display"
|
|
1729
|
+
}
|
|
1730
|
+
]
|
|
1731
|
+
},
|
|
1732
|
+
theme
|
|
1733
|
+
]
|
|
1734
|
+
};
|
|
1735
|
+
var problemSolution = {
|
|
1736
|
+
name: "problemSolution",
|
|
1737
|
+
title: "Problem and solution",
|
|
1738
|
+
kind: "block",
|
|
1739
|
+
icon: "arrows-exchange",
|
|
1740
|
+
category: "Features & storytelling",
|
|
1741
|
+
description: "The pain your buyer has today, set against how you fix it",
|
|
1742
|
+
variants: [{
|
|
1743
|
+
id: "columns",
|
|
1744
|
+
name: "Two columns",
|
|
1745
|
+
description: "Problems left, solutions right"
|
|
1746
|
+
}, {
|
|
1747
|
+
id: "pairs",
|
|
1748
|
+
name: "Paired rows",
|
|
1749
|
+
description: "Each problem sits next to its fix"
|
|
1750
|
+
}],
|
|
1751
|
+
preview: {
|
|
1752
|
+
title: "heading",
|
|
1753
|
+
icon: "arrows-exchange"
|
|
1754
|
+
},
|
|
1755
|
+
groups: CONTENT_DESIGN,
|
|
1756
|
+
fields: [
|
|
1757
|
+
{
|
|
1758
|
+
name: "eyebrow",
|
|
1759
|
+
title: "Eyebrow",
|
|
1760
|
+
type: "string",
|
|
1761
|
+
validation: { max: 32 }
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: "heading",
|
|
1765
|
+
title: "Heading",
|
|
1766
|
+
type: "string",
|
|
1767
|
+
validation: {
|
|
1768
|
+
required: true,
|
|
1769
|
+
recommendedMax: 60
|
|
1770
|
+
},
|
|
1771
|
+
ai: true,
|
|
1772
|
+
localized: true
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
name: "problem",
|
|
1776
|
+
title: "Problem side",
|
|
1777
|
+
type: "object",
|
|
1778
|
+
icon: "alert-triangle",
|
|
1779
|
+
fields: [{
|
|
1780
|
+
name: "label",
|
|
1781
|
+
title: "Column label",
|
|
1782
|
+
type: "string",
|
|
1783
|
+
initialValue: "Without Ledgerly",
|
|
1784
|
+
validation: { max: 32 },
|
|
1785
|
+
localized: true
|
|
1786
|
+
}, {
|
|
1787
|
+
name: "summary",
|
|
1788
|
+
title: "Summary",
|
|
1789
|
+
type: "text",
|
|
1790
|
+
rows: 2,
|
|
1791
|
+
localized: true
|
|
1792
|
+
}]
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
name: "solution",
|
|
1796
|
+
title: "Solution side",
|
|
1797
|
+
type: "object",
|
|
1798
|
+
icon: "circle-check",
|
|
1799
|
+
fields: [
|
|
1800
|
+
{
|
|
1801
|
+
name: "label",
|
|
1802
|
+
title: "Column label",
|
|
1803
|
+
type: "string",
|
|
1804
|
+
initialValue: "With Ledgerly",
|
|
1805
|
+
validation: { max: 32 },
|
|
1806
|
+
localized: true
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
name: "summary",
|
|
1810
|
+
title: "Summary",
|
|
1811
|
+
type: "text",
|
|
1812
|
+
rows: 2,
|
|
1813
|
+
localized: true
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
name: "button",
|
|
1817
|
+
title: "Button",
|
|
1818
|
+
type: "button"
|
|
1819
|
+
}
|
|
1820
|
+
]
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
name: "pairs",
|
|
1824
|
+
title: "Pairs",
|
|
1825
|
+
type: "array",
|
|
1826
|
+
itemNoun: "pair",
|
|
1827
|
+
validation: {
|
|
1828
|
+
min: 2,
|
|
1829
|
+
max: 6
|
|
1830
|
+
},
|
|
1831
|
+
of: [{
|
|
1832
|
+
name: "pair",
|
|
1833
|
+
title: "Pair",
|
|
1834
|
+
type: "object",
|
|
1835
|
+
icon: "arrows-exchange",
|
|
1836
|
+
preview: {
|
|
1837
|
+
title: "problem",
|
|
1838
|
+
subtitle: "solution",
|
|
1839
|
+
fallback: "Untitled pair"
|
|
1840
|
+
},
|
|
1841
|
+
fields: [
|
|
1842
|
+
{
|
|
1843
|
+
name: "problem",
|
|
1844
|
+
title: "Problem",
|
|
1845
|
+
type: "string",
|
|
1846
|
+
validation: {
|
|
1847
|
+
required: true,
|
|
1848
|
+
max: 90
|
|
1849
|
+
},
|
|
1850
|
+
ai: true,
|
|
1851
|
+
localized: true
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
name: "solution",
|
|
1855
|
+
title: "Solution",
|
|
1856
|
+
type: "string",
|
|
1857
|
+
validation: {
|
|
1858
|
+
required: true,
|
|
1859
|
+
max: 90
|
|
1860
|
+
},
|
|
1861
|
+
ai: true,
|
|
1862
|
+
localized: true
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
name: "metric",
|
|
1866
|
+
title: "Proof",
|
|
1867
|
+
type: "string",
|
|
1868
|
+
placeholder: "4 hours saved a month",
|
|
1869
|
+
validation: { max: 40 },
|
|
1870
|
+
localized: true
|
|
1871
|
+
}
|
|
1872
|
+
]
|
|
1873
|
+
}]
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
name: "tone",
|
|
1877
|
+
title: "Problem tone",
|
|
1878
|
+
type: "select",
|
|
1879
|
+
group: "design",
|
|
1880
|
+
layout: "segmented",
|
|
1881
|
+
initialValue: "muted",
|
|
1882
|
+
options: [{
|
|
1883
|
+
value: "muted",
|
|
1884
|
+
label: "Muted"
|
|
1885
|
+
}, {
|
|
1886
|
+
value: "warning",
|
|
1887
|
+
label: "Warning"
|
|
1888
|
+
}]
|
|
1889
|
+
},
|
|
1890
|
+
theme
|
|
1891
|
+
]
|
|
1892
|
+
};
|
|
1893
|
+
var integrations = {
|
|
1894
|
+
name: "integrationsGrid",
|
|
1895
|
+
title: "Integrations grid",
|
|
1896
|
+
kind: "block",
|
|
1897
|
+
icon: "plug-connected",
|
|
1898
|
+
category: "Features & storytelling",
|
|
1899
|
+
description: "Logos of the tools you connect with, filterable by category, each linking to its page",
|
|
1900
|
+
variants: [{
|
|
1901
|
+
id: "grid",
|
|
1902
|
+
name: "Logo grid",
|
|
1903
|
+
description: "Logo, name and one line per tile"
|
|
1904
|
+
}, {
|
|
1905
|
+
id: "compact",
|
|
1906
|
+
name: "Compact",
|
|
1907
|
+
description: "Logos only, names on hover"
|
|
1908
|
+
}],
|
|
1909
|
+
preview: {
|
|
1910
|
+
title: "heading",
|
|
1911
|
+
icon: "plug-connected"
|
|
1912
|
+
},
|
|
1913
|
+
groups: CONTENT_DESIGN,
|
|
1914
|
+
fields: [
|
|
1915
|
+
{
|
|
1916
|
+
name: "eyebrow",
|
|
1917
|
+
title: "Eyebrow",
|
|
1918
|
+
type: "string",
|
|
1919
|
+
validation: { max: 32 }
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
name: "heading",
|
|
1923
|
+
title: "Heading",
|
|
1924
|
+
type: "string",
|
|
1925
|
+
validation: {
|
|
1926
|
+
required: true,
|
|
1927
|
+
recommendedMax: 60
|
|
1928
|
+
},
|
|
1929
|
+
ai: true,
|
|
1930
|
+
localized: true
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
name: "subheading",
|
|
1934
|
+
title: "Subheading",
|
|
1935
|
+
type: "text",
|
|
1936
|
+
rows: 2,
|
|
1937
|
+
localized: true
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
name: "source",
|
|
1941
|
+
title: "Which integrations",
|
|
1942
|
+
type: "select",
|
|
1943
|
+
layout: "radio",
|
|
1944
|
+
initialValue: "query",
|
|
1945
|
+
options: [{
|
|
1946
|
+
value: "query",
|
|
1947
|
+
label: "From the Integrations collection",
|
|
1948
|
+
description: "New integrations appear here on their own"
|
|
1949
|
+
}, {
|
|
1950
|
+
value: "manual",
|
|
1951
|
+
label: "Pick them",
|
|
1952
|
+
description: "A fixed list in your order"
|
|
1953
|
+
}]
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
name: "categories",
|
|
1957
|
+
title: "Categories",
|
|
1958
|
+
type: "array",
|
|
1959
|
+
hidden: {
|
|
1960
|
+
field: "source",
|
|
1961
|
+
equals: "query"
|
|
1962
|
+
},
|
|
1963
|
+
of: [{
|
|
1964
|
+
name: "category",
|
|
1965
|
+
title: "Category",
|
|
1966
|
+
type: "reference",
|
|
1967
|
+
reference: { to: ["col_integration_categories"] }
|
|
1968
|
+
}],
|
|
1969
|
+
description: "Leave empty for all categories."
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
name: "showFeaturedOnly",
|
|
1973
|
+
title: "Featured only",
|
|
1974
|
+
type: "boolean",
|
|
1975
|
+
hidden: {
|
|
1976
|
+
field: "source",
|
|
1977
|
+
equals: "query"
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
name: "limit",
|
|
1982
|
+
title: "How many",
|
|
1983
|
+
type: "number",
|
|
1984
|
+
hidden: {
|
|
1985
|
+
field: "source",
|
|
1986
|
+
equals: "query"
|
|
1987
|
+
},
|
|
1988
|
+
initialValue: 24,
|
|
1989
|
+
numeric: {
|
|
1990
|
+
min: 4,
|
|
1991
|
+
max: 96,
|
|
1992
|
+
step: 4
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
name: "sort",
|
|
1997
|
+
title: "Sort by",
|
|
1998
|
+
type: "select",
|
|
1999
|
+
hidden: {
|
|
2000
|
+
field: "source",
|
|
2001
|
+
equals: "query"
|
|
2002
|
+
},
|
|
2003
|
+
initialValue: "popular",
|
|
2004
|
+
options: [
|
|
2005
|
+
{
|
|
2006
|
+
value: "popular",
|
|
2007
|
+
label: "Most used"
|
|
2008
|
+
},
|
|
2009
|
+
{
|
|
2010
|
+
value: "az",
|
|
2011
|
+
label: "A to Z"
|
|
2012
|
+
},
|
|
2013
|
+
{
|
|
2014
|
+
value: "new",
|
|
2015
|
+
label: "Newest"
|
|
2016
|
+
}
|
|
2017
|
+
]
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
name: "items",
|
|
2021
|
+
title: "Integrations",
|
|
2022
|
+
type: "array",
|
|
2023
|
+
itemNoun: "integration",
|
|
2024
|
+
arrayLayout: "grid",
|
|
2025
|
+
hidden: {
|
|
2026
|
+
field: "source",
|
|
2027
|
+
equals: "manual"
|
|
2028
|
+
},
|
|
2029
|
+
validation: {
|
|
2030
|
+
min: 4,
|
|
2031
|
+
max: 48
|
|
2032
|
+
},
|
|
2033
|
+
of: [{
|
|
2034
|
+
name: "integration",
|
|
2035
|
+
title: "Integration",
|
|
2036
|
+
type: "reference",
|
|
2037
|
+
reference: { to: ["col_integrations"] }
|
|
2038
|
+
}]
|
|
2039
|
+
},
|
|
2040
|
+
{
|
|
2041
|
+
name: "showFilter",
|
|
2042
|
+
title: "Show category filter",
|
|
2043
|
+
type: "boolean",
|
|
2044
|
+
initialValue: true
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
name: "showSearch",
|
|
2048
|
+
title: "Show search",
|
|
2049
|
+
type: "boolean"
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
name: "viewAllButton",
|
|
2053
|
+
title: "View all button",
|
|
2054
|
+
type: "button",
|
|
2055
|
+
description: "Hidden when everything already fits."
|
|
2056
|
+
},
|
|
2057
|
+
{
|
|
2058
|
+
name: "columns",
|
|
2059
|
+
title: "Columns on desktop",
|
|
2060
|
+
type: "number",
|
|
2061
|
+
group: "design",
|
|
2062
|
+
layout: "slider",
|
|
2063
|
+
initialValue: 4,
|
|
2064
|
+
numeric: {
|
|
2065
|
+
min: 3,
|
|
2066
|
+
max: 6,
|
|
2067
|
+
step: 1
|
|
2068
|
+
}
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "isMonochrome",
|
|
2072
|
+
title: "Monochrome logos",
|
|
2073
|
+
type: "boolean",
|
|
2074
|
+
group: "design"
|
|
2075
|
+
},
|
|
2076
|
+
theme
|
|
2077
|
+
]
|
|
2078
|
+
};
|
|
2079
|
+
var ENTRIES = [
|
|
2080
|
+
{
|
|
2081
|
+
id: "feature-icon-grid",
|
|
2082
|
+
label: "Icon feature grid",
|
|
2083
|
+
proves: "Bounded repeater · per-item optional link · column count as a variant, not a number field",
|
|
2084
|
+
category: "Features & storytelling",
|
|
2085
|
+
tags: [
|
|
2086
|
+
"repeater",
|
|
2087
|
+
"variants",
|
|
2088
|
+
"validation",
|
|
2089
|
+
"localized"
|
|
2090
|
+
],
|
|
2091
|
+
brand: "Caldera",
|
|
2092
|
+
previewLayout: "grid-3",
|
|
2093
|
+
schema: featureGrid,
|
|
2094
|
+
value: {
|
|
2095
|
+
_variant: "three",
|
|
2096
|
+
eyebrow: "Why cast iron",
|
|
2097
|
+
heading: "Gear that gets better every time you cook.",
|
|
2098
|
+
intro: "Every Caldera piece is poured, machined and seasoned in Bend, Oregon — then guaranteed for life.",
|
|
2099
|
+
features: [
|
|
2100
|
+
{
|
|
2101
|
+
_key: "f1",
|
|
2102
|
+
icon: "flame",
|
|
2103
|
+
title: "Rated to 900°F",
|
|
2104
|
+
body: "Straight onto coals, into a pizza oven or under the broiler. No coatings to burn off.",
|
|
2105
|
+
link: {
|
|
2106
|
+
type: "page",
|
|
2107
|
+
href: "/journal/heat-ratings",
|
|
2108
|
+
label: "How we test",
|
|
2109
|
+
title: "Heat ratings"
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2112
|
+
{
|
|
2113
|
+
_key: "f2",
|
|
2114
|
+
icon: "feather",
|
|
2115
|
+
title: "40% lighter",
|
|
2116
|
+
body: "Thin-wall casting gets a 10\" skillet under four pounds without losing heat retention."
|
|
2117
|
+
},
|
|
2118
|
+
{
|
|
2119
|
+
_key: "f3",
|
|
2120
|
+
icon: "droplet",
|
|
2121
|
+
title: "Pre-seasoned three times",
|
|
2122
|
+
body: "Flaxseed and grapeseed oil, baked on in layers. Cook eggs on day one."
|
|
2123
|
+
},
|
|
2124
|
+
{
|
|
2125
|
+
_key: "f4",
|
|
2126
|
+
icon: "shield-check",
|
|
2127
|
+
title: "Lifetime warranty",
|
|
2128
|
+
body: "Crack it, warp it, lose the handle — we replace it. No receipt needed.",
|
|
2129
|
+
link: {
|
|
2130
|
+
type: "page",
|
|
2131
|
+
href: "/warranty",
|
|
2132
|
+
label: "Read the warranty",
|
|
2133
|
+
title: "Warranty"
|
|
2134
|
+
}
|
|
2135
|
+
},
|
|
2136
|
+
{
|
|
2137
|
+
_key: "f5",
|
|
2138
|
+
icon: "recycle",
|
|
2139
|
+
title: "Recycled iron",
|
|
2140
|
+
body: "92% of our iron is reclaimed from scrap engine blocks and radiators."
|
|
2141
|
+
},
|
|
2142
|
+
{
|
|
2143
|
+
_key: "f6",
|
|
2144
|
+
icon: "backpack",
|
|
2145
|
+
title: "Packs flat",
|
|
2146
|
+
body: "Folding handles and nesting lids fit a 45-litre pack with room for food."
|
|
2147
|
+
}
|
|
2148
|
+
],
|
|
2149
|
+
button: {
|
|
2150
|
+
label: "Compare cookware",
|
|
2151
|
+
href: "/products",
|
|
2152
|
+
type: "page",
|
|
2153
|
+
title: "Products",
|
|
2154
|
+
style: "outline",
|
|
2155
|
+
arrow: true
|
|
2156
|
+
},
|
|
2157
|
+
iconStyle: "tile",
|
|
2158
|
+
alignment: "left",
|
|
2159
|
+
showDividers: false,
|
|
2160
|
+
theme: "light"
|
|
2161
|
+
},
|
|
2162
|
+
notes: {
|
|
2163
|
+
why: "Features are an ARRAY OF OBJECTS because they are owned by this section and never reused elsewhere — if the same benefits appear on five product pages, promote them to a `col_features` collection and switch the array to references. Column count is a VARIANT (3/4/list), not a number, because the layout changes (icon beside vs above text), and variants swap without losing content. The per-feature link is a `link` (not a button) because it is a quiet text link; the section CTA is a `button`. Icon style and dividers are design and live in the Design group.",
|
|
2164
|
+
sanity: "object `featureGrid` with `features: array<object{icon (sanity-plugin-icon-picker), title, body, link}>`, layout via a `string` field with `options.list` (Sanity has no first-class variants).",
|
|
2165
|
+
htmlSignals: [
|
|
2166
|
+
"A heading followed by a grid (`display:grid` / `grid-cols-3|4`) of 3–12 sibling cards with identical structure",
|
|
2167
|
+
"Each card starts with an `<svg>` or icon font `<i>` (24–48px) before an `<h3>`/`<h4>` and one `<p>`",
|
|
2168
|
+
"No images larger than 64px inside the cards (that would be a card grid, not a feature grid)",
|
|
2169
|
+
"Optional trailing `<a>` per card with text like “Learn more” / “Read more”"
|
|
2170
|
+
],
|
|
2171
|
+
mcp: "Map the section `<h2>` → heading, the small uppercase text above it → eyebrow, the first `<p>` after it → intro. For each repeated card: SVG/icon → `icon` (match to the nearest Tabler name by the icon's `aria-label`, class name like `ti-flame`/`lucide-flame`, or path similarity; fall back to `sparkles`), `<h3>` → title, `<p>` → body, trailing anchor → link (`type: \"url\"` for absolute hrefs, `page` when the href resolves to a known page). Pick the variant from the computed column count at desktop width (3 → three, 4 → four, 2 with icon beside text → list). Do not create fields for card background, padding or icon color — they are theme tokens.",
|
|
2172
|
+
pitfalls: [
|
|
2173
|
+
"Do not invent a `columns` number field — column count is the variant; a free number lets editors break the layout.",
|
|
2174
|
+
"Feature cards with large photos are an image card grid, not this block.",
|
|
2175
|
+
"Icon fonts with ligatures (Material Symbols) store the icon as TEXT content — read the text, not the class."
|
|
2176
|
+
]
|
|
2177
|
+
}
|
|
2178
|
+
},
|
|
2179
|
+
{
|
|
2180
|
+
id: "feature-alternating-rows",
|
|
2181
|
+
label: "Alternating feature rows",
|
|
2182
|
+
proves: "Per-row media type (image or video) via conditional fields · auto-alternating side with per-row override",
|
|
2183
|
+
category: "Features & storytelling",
|
|
2184
|
+
tags: [
|
|
2185
|
+
"repeater",
|
|
2186
|
+
"conditional",
|
|
2187
|
+
"media-heavy",
|
|
2188
|
+
"variants",
|
|
2189
|
+
"rich-text"
|
|
2190
|
+
],
|
|
2191
|
+
brand: "Caldera",
|
|
2192
|
+
previewLayout: "split",
|
|
2193
|
+
schema: featureRows,
|
|
2194
|
+
value: {
|
|
2195
|
+
_variant: "zigzag",
|
|
2196
|
+
heading: "Engineered at the foundry, proven at the fire.",
|
|
2197
|
+
rows: [
|
|
2198
|
+
{
|
|
2199
|
+
_key: "r1",
|
|
2200
|
+
eyebrow: "The pour",
|
|
2201
|
+
title: "Thin walls that still hold heat like a brick.",
|
|
2202
|
+
body: "<p>We pour at 2,600°F into <strong>sand moulds we make by hand</strong>, then machine the cooking surface smooth. The result is a pan that heats evenly in four minutes and stays hot when the steak goes in.</p>",
|
|
2203
|
+
points: [
|
|
2204
|
+
"3.8 lb for a 10\" skillet",
|
|
2205
|
+
"Machined, glass-smooth surface",
|
|
2206
|
+
"Poured and finished in Bend, OR"
|
|
2207
|
+
],
|
|
2208
|
+
button: {
|
|
2209
|
+
label: "See the Ridge Skillet",
|
|
2210
|
+
href: "/products/ridge-skillet",
|
|
2211
|
+
type: "entry",
|
|
2212
|
+
title: "Ridge Skillet 10\"",
|
|
2213
|
+
style: "link",
|
|
2214
|
+
arrow: true
|
|
2215
|
+
},
|
|
2216
|
+
mediaType: "image",
|
|
2217
|
+
image: img("caldera-foundry-pour", "Molten iron pouring into a sand mould at the Bend foundry", 1200, 900, {
|
|
2218
|
+
x: 52,
|
|
2219
|
+
y: 40
|
|
2220
|
+
}),
|
|
2221
|
+
side: "auto"
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
_key: "r2",
|
|
2225
|
+
eyebrow: "The fold",
|
|
2226
|
+
title: "A grill that folds to the size of a laptop.",
|
|
2227
|
+
body: "<p>The Cable Fold collapses along two stainless hinges and slides into its own canvas sleeve. Unfolded, it holds four steaks over a fire ring.</p>",
|
|
2228
|
+
points: ["Folds to 14 × 10 × 1.5 in", "Stainless hinges rated for 10,000 cycles"],
|
|
2229
|
+
button: {
|
|
2230
|
+
label: "Shop the Cable Fold",
|
|
2231
|
+
href: "/products/cable-fold",
|
|
2232
|
+
type: "entry",
|
|
2233
|
+
title: "Cable Fold Grill",
|
|
2234
|
+
style: "link",
|
|
2235
|
+
arrow: true
|
|
2236
|
+
},
|
|
2237
|
+
mediaType: "video",
|
|
2238
|
+
video: {
|
|
2239
|
+
url: "https://cdn.caldera.co/film/cable-fold-unfold-loop.mp4",
|
|
2240
|
+
poster: "https://picsum.photos/seed/cable-fold-loop/1200/900"
|
|
2241
|
+
},
|
|
2242
|
+
side: "auto"
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
_key: "r3",
|
|
2246
|
+
eyebrow: "The seasoning",
|
|
2247
|
+
title: "Non-stick from the first egg.",
|
|
2248
|
+
body: "<p>Three coats of flaxseed and grapeseed oil, each baked at 500°F. No PFAS, no coatings, nothing to flake off.</p>",
|
|
2249
|
+
points: [],
|
|
2250
|
+
mediaType: "image",
|
|
2251
|
+
image: img("caldera-seasoning-egg", "A fried egg sliding off a freshly seasoned skillet", 1200, 900),
|
|
2252
|
+
side: "auto"
|
|
2253
|
+
}
|
|
2254
|
+
],
|
|
2255
|
+
startSide: "right",
|
|
2256
|
+
mediaRatio: "4:3",
|
|
2257
|
+
theme: "light"
|
|
2258
|
+
},
|
|
2259
|
+
notes: {
|
|
2260
|
+
why: "Rows are an ARRAY OF OBJECTS — each row is a mini-section with its own media, so a slot would be overkill and would let editors drop unrelated blocks in. The image side is DERIVED (section `startSide` + row index) and only stored when an editor overrides it (`side: auto|left|right`), so reordering rows keeps the zigzag intact. Image vs video is a `mediaType` select with conditional fields rather than two optional fields, so exactly one is required and validation is honest. Bullet points are a primitive string array, not rich text, so the checkmark list renders consistently.",
|
|
2261
|
+
sanity: "array of `featureRow` objects with `media` as a union (`image` | `mux.video`) via a `mediaType` string + `hidden: ({parent}) => parent.mediaType !== 'image'`; `side` as a string list.",
|
|
2262
|
+
htmlSignals: [
|
|
2263
|
+
"Two or more consecutive blocks each with a 2-column layout (text + media) where the DOM order or `flex-direction: row-reverse` / `order-last` flips on alternating rows",
|
|
2264
|
+
"Each block has its own `<h2>`/`<h3>`, a `<p>` and often a `<ul>` with check icons",
|
|
2265
|
+
"Media is an `<img>`/`<picture>` or a muted `<video autoplay loop playsinline>`",
|
|
2266
|
+
"Class names like `feature-row`, `zigzag`, `media-text`, `lg:flex-row-reverse` on every second row"
|
|
2267
|
+
],
|
|
2268
|
+
mcp: "Group consecutive text+media rows into ONE featureRows block (not one block per row). Per row: `<h2|h3>` → title, small text above → eyebrow, paragraphs → body (keep `<strong>`, `<em>`, `<a>`; drop wrappers), `<ul>` items → points (strip icons), anchor/button → button. `<video>` → mediaType video + video.url from `<source src>` and poster from `poster=`; else image with `alt` from the img. Detect `startSide` from where the FIRST row's media sits; only set a row's `side` when it breaks the alternation. Media aspect ratio → mediaRatio (round to the nearest option).",
|
|
2269
|
+
pitfalls: ["Storing `side` on every row breaks the zigzag the moment rows are reordered — store `auto` unless the source deliberately breaks the pattern.", "Autoplay videos without a poster flash black; always extract or generate a poster."]
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
id: "bento-feature-showcase",
|
|
2274
|
+
label: "Feature bento (responsive spans)",
|
|
2275
|
+
proves: "Slot of blocks with per-breakpoint span, phone order and conditional background in slotSettings",
|
|
2276
|
+
category: "Features & storytelling",
|
|
2277
|
+
tags: [
|
|
2278
|
+
"slots",
|
|
2279
|
+
"responsive-layout",
|
|
2280
|
+
"conditional",
|
|
2281
|
+
"media-heavy",
|
|
2282
|
+
"variants"
|
|
2283
|
+
],
|
|
2284
|
+
brand: "Ledgerly",
|
|
2285
|
+
previewLayout: "bento",
|
|
2286
|
+
schema: featureBento,
|
|
2287
|
+
value: {
|
|
2288
|
+
_variant: "grid-4",
|
|
2289
|
+
eyebrow: "Product tour",
|
|
2290
|
+
heading: "Close the month in a day, not a week.",
|
|
2291
|
+
subheading: "Ledgerly reconciles bank feeds, chases invoices and drafts your books while you run the business.",
|
|
2292
|
+
cells: [
|
|
2293
|
+
{
|
|
2294
|
+
_key: "c1",
|
|
2295
|
+
_type: "imageBlock",
|
|
2296
|
+
_variant: "contain",
|
|
2297
|
+
_settings: {
|
|
2298
|
+
desktopWidth: 2,
|
|
2299
|
+
rowsDesktop: 2,
|
|
2300
|
+
tabletWidth: "full",
|
|
2301
|
+
mobileOrder: 1,
|
|
2302
|
+
surface: "muted"
|
|
2303
|
+
},
|
|
2304
|
+
image: img("ledgerly-reconcile-ui", "Bank reconciliation screen matching 48 transactions automatically", 1200, 1e3),
|
|
2305
|
+
caption: "Auto-match 95% of bank lines"
|
|
2306
|
+
},
|
|
2307
|
+
{
|
|
2308
|
+
_key: "c2",
|
|
2309
|
+
_type: "statBlock",
|
|
2310
|
+
_settings: {
|
|
2311
|
+
desktopWidth: 1,
|
|
2312
|
+
rowsDesktop: 1,
|
|
2313
|
+
tabletWidth: "half",
|
|
2314
|
+
mobileOrder: 3,
|
|
2315
|
+
surface: "brand"
|
|
2316
|
+
},
|
|
2317
|
+
value: "6 hrs",
|
|
2318
|
+
label: "Saved per close, on average",
|
|
2319
|
+
icon: "clock"
|
|
2320
|
+
},
|
|
2321
|
+
{
|
|
2322
|
+
_key: "c3",
|
|
2323
|
+
_type: "featureBlock",
|
|
2324
|
+
_settings: {
|
|
2325
|
+
desktopWidth: 1,
|
|
2326
|
+
rowsDesktop: 1,
|
|
2327
|
+
tabletWidth: "half",
|
|
2328
|
+
mobileOrder: 4,
|
|
2329
|
+
surface: "muted"
|
|
2330
|
+
},
|
|
2331
|
+
icon: "receipt",
|
|
2332
|
+
title: "Receipts by email",
|
|
2333
|
+
body: "Forward a receipt to your inbox address and it lands on the right transaction."
|
|
2334
|
+
},
|
|
2335
|
+
{
|
|
2336
|
+
_key: "c4",
|
|
2337
|
+
_type: "videoBlock",
|
|
2338
|
+
_settings: {
|
|
2339
|
+
desktopWidth: 2,
|
|
2340
|
+
rowsDesktop: 1,
|
|
2341
|
+
tabletWidth: "full",
|
|
2342
|
+
mobileOrder: 2,
|
|
2343
|
+
surface: "dark"
|
|
2344
|
+
},
|
|
2345
|
+
title: "Invoice reminders that sound like you",
|
|
2346
|
+
video: { url: "https://www.youtube.com/watch?v=ledgerly-reminders" },
|
|
2347
|
+
poster: img("ledgerly-reminders", "Invoice reminder email preview", 1200, 600),
|
|
2348
|
+
autoplay: true
|
|
2349
|
+
},
|
|
2350
|
+
{
|
|
2351
|
+
_key: "c5",
|
|
2352
|
+
_type: "quoteBlock",
|
|
2353
|
+
_settings: {
|
|
2354
|
+
desktopWidth: 2,
|
|
2355
|
+
rowsDesktop: 1,
|
|
2356
|
+
tabletWidth: "full",
|
|
2357
|
+
surface: "image",
|
|
2358
|
+
background: img("ledgerly-bakery", "Bakery counter at opening time", 1200, 600)
|
|
2359
|
+
},
|
|
2360
|
+
quote: "We stopped paying our bookkeeper overtime in March. That's the whole review.",
|
|
2361
|
+
author: "Rosa Delgado",
|
|
2362
|
+
role: "Owner, Masa Bakery",
|
|
2363
|
+
rating: 5
|
|
2364
|
+
}
|
|
2365
|
+
],
|
|
2366
|
+
button: {
|
|
2367
|
+
label: "Start a free trial",
|
|
2368
|
+
href: "/signup",
|
|
2369
|
+
type: "page",
|
|
2370
|
+
title: "Sign up",
|
|
2371
|
+
style: "primary",
|
|
2372
|
+
arrow: true
|
|
2373
|
+
},
|
|
2374
|
+
gap: "md",
|
|
2375
|
+
radius: "lg",
|
|
2376
|
+
theme: "light"
|
|
2377
|
+
},
|
|
2378
|
+
notes: {
|
|
2379
|
+
why: "The cells are a SLOT (blocks inside a block) because bento cells are genuinely heterogeneous — a screenshot, a stat, a quote, a clip — and each needs its own schema. Unlike the core bento (one `span` preset), sizing here is RESPONSIVE: desktop column/row spans, a tablet width and a phone order are stored in `_settings` on each child, so the cell's CONTENT stays clean and the layout data travels with the cell when it is reordered. The background image is a slot setting (layout/surface), not cell content, and only appears when surface = image.",
|
|
2380
|
+
sanity: "array of block types (`of: [{type:'featureCard'},{type:'statCard'},…]`) with a shared `layout` object field on each member type (`colSpan`, `rowSpan`, `mobileOrder`) — Sanity has no slot settings, so layout is duplicated on every member type.",
|
|
2381
|
+
htmlSignals: [
|
|
2382
|
+
"A CSS grid container with children using different `grid-column: span N` / `col-span-2` / `row-span-2` classes",
|
|
2383
|
+
"Children of different internal shapes (one has a big image, one a giant number, one a blockquote)",
|
|
2384
|
+
"Breakpoint-prefixed span classes (`md:col-span-2 lg:col-span-3`) or `order-*` classes for mobile",
|
|
2385
|
+
"Rounded tiles with differing backgrounds (dark, brand color, photo)"
|
|
2386
|
+
],
|
|
2387
|
+
mcp: "Treat each grid child as a slot child; classify by content: big number + short label → statBlock, img/video dominant → imageBlock/videoBlock, blockquote/quote marks → quoteBlock, icon+h3+p → featureBlock, heading+button → ctaCard, else textBlock. Read spans from computed `grid-column-end`/`grid-row-end` at 1280px → desktopWidth/rowsDesktop, at 768px → tabletWidth (full when it spans every column). DOM order vs visual order on a 375px viewport → mobileOrder only when they differ. Background color → surface (match to brand tokens; photo backgrounds → surface image + background).",
|
|
2388
|
+
pitfalls: [
|
|
2389
|
+
"Do not put span values on the child block's own fields — reusing that block elsewhere would carry a meaningless span.",
|
|
2390
|
+
"Phones stack cells; if the source reorders them with CSS `order`, capture it or the story reads out of order.",
|
|
2391
|
+
"6-column variants need spans up to 6; validate spans against the variant's column count."
|
|
2392
|
+
]
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
{
|
|
2396
|
+
id: "steps-how-it-works",
|
|
2397
|
+
label: "Steps / how it works",
|
|
2398
|
+
proves: "Order is the number (never stored) · optional per-step image used by one variant only",
|
|
2399
|
+
category: "Features & storytelling",
|
|
2400
|
+
tags: [
|
|
2401
|
+
"repeater",
|
|
2402
|
+
"variants",
|
|
2403
|
+
"validation"
|
|
2404
|
+
],
|
|
2405
|
+
brand: "Caldera",
|
|
2406
|
+
previewLayout: "grid-4",
|
|
2407
|
+
schema: steps,
|
|
2408
|
+
value: {
|
|
2409
|
+
_variant: "row",
|
|
2410
|
+
eyebrow: "Lifetime exchange",
|
|
2411
|
+
heading: "Wore it out? Send it home.",
|
|
2412
|
+
steps: [
|
|
2413
|
+
{
|
|
2414
|
+
_key: "s1",
|
|
2415
|
+
title: "Start a claim",
|
|
2416
|
+
body: "Tell us what happened and add a photo. No receipt, no proof of purchase.",
|
|
2417
|
+
icon: "message-circle",
|
|
2418
|
+
duration: "2 minutes",
|
|
2419
|
+
link: {
|
|
2420
|
+
type: "page",
|
|
2421
|
+
href: "/warranty/claim",
|
|
2422
|
+
label: "Start a claim",
|
|
2423
|
+
title: "Warranty claim"
|
|
2424
|
+
}
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
_key: "s2",
|
|
2428
|
+
title: "Print the label",
|
|
2429
|
+
body: "We email a prepaid label. Drop the box at any UPS store.",
|
|
2430
|
+
icon: "printer",
|
|
2431
|
+
duration: "Same day"
|
|
2432
|
+
},
|
|
2433
|
+
{
|
|
2434
|
+
_key: "s3",
|
|
2435
|
+
title: "We restore it",
|
|
2436
|
+
body: "Our foundry team strips, re-machines and reseasons it — or casts you a new one.",
|
|
2437
|
+
icon: "tool",
|
|
2438
|
+
duration: "10 days"
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
_key: "s4",
|
|
2442
|
+
title: "Cook again",
|
|
2443
|
+
body: "It ships back to you free, seasoned and ready for the next fire.",
|
|
2444
|
+
icon: "flame",
|
|
2445
|
+
duration: "3–5 days"
|
|
2446
|
+
}
|
|
2447
|
+
],
|
|
2448
|
+
button: {
|
|
2449
|
+
label: "Read the full warranty",
|
|
2450
|
+
href: "/warranty",
|
|
2451
|
+
type: "page",
|
|
2452
|
+
title: "Warranty",
|
|
2453
|
+
style: "outline"
|
|
2454
|
+
},
|
|
2455
|
+
numberStyle: "padded",
|
|
2456
|
+
showConnector: true,
|
|
2457
|
+
theme: "light"
|
|
2458
|
+
},
|
|
2459
|
+
notes: {
|
|
2460
|
+
why: "Steps are an ORDERED ARRAY; the step number is computed from the index so drag-to-reorder can never desync it. `duration` is a short string (\"10 days\", \"Day 3\") rather than a number+unit pair because it is display copy, not data anyone filters on. The per-step image is optional and only rendered by the Vertical variant — keeping it on the item (instead of a separate array) means switching variants never loses content.",
|
|
2461
|
+
sanity: "array of `step` objects; ordering is the array order; Sanity has no built-in index rendering so numbers come from the frontend.",
|
|
2462
|
+
htmlSignals: [
|
|
2463
|
+
"An `<ol>` (or divs) of 2–6 items each starting with a large number (“1”, “01”, “Step 1”) or a numbered circle",
|
|
2464
|
+
"Connector lines between items (`::before`/`::after` borders, SVG dashes)",
|
|
2465
|
+
"Headings like “How it works”, “Getting started”, “What happens next”"
|
|
2466
|
+
],
|
|
2467
|
+
mcp: "Strip the visible number from each step's title (“01 — Start a claim” → “Start a claim”) and set numberStyle from its format (1 → digits, 01 → padded, none visible → none). Per step: heading → title, paragraph → body, small pill/meta text (“2 min”) → duration, icon → icon. Pick the variant from the layout direction (horizontal → row, vertical with images → stack, bordered tiles → cards). A dashed line/border between numbers → showConnector true.",
|
|
2468
|
+
pitfalls: ["Never store the number as content — the converter must strip it, otherwise reordering shows “3, 1, 2”.", "HowTo rich results were retired by Google; do not emit HowTo JSON-LD for this block."]
|
|
2469
|
+
}
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
id: "timeline-company-history",
|
|
2473
|
+
label: "Timeline / company history",
|
|
2474
|
+
proves: "Date precision select drives which date field shows · display order derived from the date, not the array",
|
|
2475
|
+
category: "Features & storytelling",
|
|
2476
|
+
tags: [
|
|
2477
|
+
"repeater",
|
|
2478
|
+
"conditional",
|
|
2479
|
+
"media-heavy",
|
|
2480
|
+
"validation",
|
|
2481
|
+
"rich-text"
|
|
2482
|
+
],
|
|
2483
|
+
brand: "Caldera",
|
|
2484
|
+
previewLayout: "timeline",
|
|
2485
|
+
schema: timeline,
|
|
2486
|
+
value: {
|
|
2487
|
+
_variant: "vertical",
|
|
2488
|
+
eyebrow: "Our story",
|
|
2489
|
+
heading: "Twelve years of fires, foundries and second chances.",
|
|
2490
|
+
intro: "From a garage in Bend to a foundry that pours 4,000 pans a week.",
|
|
2491
|
+
milestones: [
|
|
2492
|
+
{
|
|
2493
|
+
_key: "m1",
|
|
2494
|
+
precision: "year",
|
|
2495
|
+
year: 2014,
|
|
2496
|
+
title: "The first skillet",
|
|
2497
|
+
body: "<p>Ana Ruiz pours a 10\" skillet in her garage using scrap from a <a href=\"/journal/engine-blocks\">1978 Ford engine block</a>.</p>",
|
|
2498
|
+
image: img("caldera-garage-2014", "Ana Ruiz holding the first Caldera skillet in her garage", 800, 600),
|
|
2499
|
+
isHighlighted: true
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
_key: "m2",
|
|
2503
|
+
precision: "month",
|
|
2504
|
+
date: "2016-04-01",
|
|
2505
|
+
label: "Spring 2016",
|
|
2506
|
+
title: "The Kickstarter",
|
|
2507
|
+
body: "<p>2,140 backers fund the first production run of the Ridge Skillet in 31 days.</p>"
|
|
2508
|
+
},
|
|
2509
|
+
{
|
|
2510
|
+
_key: "m3",
|
|
2511
|
+
precision: "year",
|
|
2512
|
+
year: 2019,
|
|
2513
|
+
title: "Our own foundry",
|
|
2514
|
+
body: "<p>We move into a 22,000 sq ft foundry on Bend's east side and hire our first twelve casters.</p>",
|
|
2515
|
+
image: img("caldera-foundry-2019", "The Caldera foundry floor with sand moulds in rows", 800, 600)
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
_key: "m4",
|
|
2519
|
+
precision: "day",
|
|
2520
|
+
date: "2022-09-14",
|
|
2521
|
+
title: "Cable Fold launches",
|
|
2522
|
+
body: "<p>Our first folding grill sells out in nine hours.</p>"
|
|
2523
|
+
},
|
|
2524
|
+
{
|
|
2525
|
+
_key: "m5",
|
|
2526
|
+
precision: "year",
|
|
2527
|
+
year: 2025,
|
|
2528
|
+
title: "Lifetime exchange, 10,000th pan",
|
|
2529
|
+
body: "<p>We restore our ten-thousandth returned pan — a 2015 skillet that had cooked across four continents.</p>",
|
|
2530
|
+
isHighlighted: true
|
|
2531
|
+
}
|
|
2532
|
+
],
|
|
2533
|
+
order: "asc",
|
|
2534
|
+
theme: "light"
|
|
2535
|
+
},
|
|
2536
|
+
notes: {
|
|
2537
|
+
why: "Milestones are an array of objects owned by the About page — history is not reused, so no collection. Date PRECISION is modelled explicitly: most history only knows a year, so a single `date` field would force fake “Jan 1” dates. A `year` number shows for year precision and a real `date` for month/day; `label` is an optional display override (\"Spring 2016\") while sorting still uses the real value. The page sorts by date, so editors can append without hunting for the right slot.",
|
|
2538
|
+
sanity: "array of `milestone` objects; Sanity would typically use a `date` plus a `string` precision, or just a `number` year; `hidden` callbacks on the precision field.",
|
|
2539
|
+
htmlSignals: [
|
|
2540
|
+
"A vertical line (`border-left`, `::before` with full height) with dots/markers and items on alternating sides",
|
|
2541
|
+
"Each item leads with a year or date in a large or monospace style (“2014”, “Spring 2016”)",
|
|
2542
|
+
"`<time datetime=…>` elements, or `<ol class=\"timeline\">`",
|
|
2543
|
+
"Horizontal scroll containers with year markers (`overflow-x: auto` + `scroll-snap`)"
|
|
2544
|
+
],
|
|
2545
|
+
mcp: "Parse each item's date text: a bare 4-digit year → precision year + `year`; “Month YYYY” → month + date YYYY-MM-01; full dates → day. Seasonal or fuzzy text (\"Spring 2016\", \"Early days\") → keep the parsed date AND put the original text into `label`. Prefer `<time datetime>` when present. Item heading → title, paragraphs → body (keep links). Larger or emphasized items → isHighlighted. Detect order by comparing the first and last dates.",
|
|
2546
|
+
pitfalls: ["Storing “2014” in a date field becomes 2014-01-01 and later renders as “Jan 1, 2014”.", "Two milestones with the same year keep their array order as the tie-breaker — keep the array roughly sorted."]
|
|
2547
|
+
}
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
id: "tabs-with-media",
|
|
2551
|
+
label: "Tabs with media",
|
|
2552
|
+
proves: "Fixed-shape tabs (no slot) · image/video conditional per tab · interval shown only when auto-advance is on",
|
|
2553
|
+
category: "Features & storytelling",
|
|
2554
|
+
tags: [
|
|
2555
|
+
"repeater",
|
|
2556
|
+
"conditional",
|
|
2557
|
+
"media-heavy",
|
|
2558
|
+
"variants",
|
|
2559
|
+
"a11y",
|
|
2560
|
+
"localized"
|
|
2561
|
+
],
|
|
2562
|
+
brand: "Ledgerly",
|
|
2563
|
+
previewLayout: "tabs",
|
|
2564
|
+
schema: mediaTabs,
|
|
2565
|
+
value: {
|
|
2566
|
+
_variant: "side",
|
|
2567
|
+
eyebrow: "How teams use Ledgerly",
|
|
2568
|
+
heading: "One ledger for everyone who touches the money.",
|
|
2569
|
+
tabs: [
|
|
2570
|
+
{
|
|
2571
|
+
_key: "t1",
|
|
2572
|
+
label: "Owners",
|
|
2573
|
+
icon: "building-store",
|
|
2574
|
+
summary: "Cash position and runway, every morning.",
|
|
2575
|
+
title: "Know your runway before coffee.",
|
|
2576
|
+
body: "<p>A daily digest with cash on hand, money owed to you and what's due this week. <strong>No logins, no spreadsheets.</strong></p>",
|
|
2577
|
+
button: {
|
|
2578
|
+
label: "See the daily digest",
|
|
2579
|
+
href: "/features/digest",
|
|
2580
|
+
type: "page",
|
|
2581
|
+
title: "Digest",
|
|
2582
|
+
style: "link",
|
|
2583
|
+
arrow: true
|
|
2584
|
+
},
|
|
2585
|
+
mediaType: "image",
|
|
2586
|
+
screenshot: img("ledgerly-digest", "Daily digest email showing cash on hand and upcoming bills", 1600, 1e3)
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
_key: "t2",
|
|
2590
|
+
label: "Bookkeepers",
|
|
2591
|
+
icon: "calculator",
|
|
2592
|
+
summary: "Reconcile 20 clients from one inbox.",
|
|
2593
|
+
title: "Every client, one reconciliation queue.",
|
|
2594
|
+
body: "<p>Switch clients with one key. Rules you write for one client can be shared with the rest.</p>",
|
|
2595
|
+
mediaType: "video",
|
|
2596
|
+
video: { url: "https://cdn.ledgerly.com/tours/bookkeeper-queue.mp4" },
|
|
2597
|
+
poster: img("ledgerly-queue-poster", "Reconciliation queue across client accounts", 1600, 1e3)
|
|
2598
|
+
},
|
|
2599
|
+
{
|
|
2600
|
+
_key: "t3",
|
|
2601
|
+
label: "Accountants",
|
|
2602
|
+
icon: "file-certificate",
|
|
2603
|
+
summary: "Year-end packs without the chase.",
|
|
2604
|
+
title: "Tax-ready books, with the receipts attached.",
|
|
2605
|
+
body: "<p>Export a year-end pack with every transaction linked to its receipt and every adjustment explained.</p>",
|
|
2606
|
+
button: {
|
|
2607
|
+
label: "Partner program",
|
|
2608
|
+
href: "/partners",
|
|
2609
|
+
type: "page",
|
|
2610
|
+
title: "Partners",
|
|
2611
|
+
style: "link",
|
|
2612
|
+
arrow: true
|
|
2613
|
+
},
|
|
2614
|
+
mediaType: "image",
|
|
2615
|
+
screenshot: img("ledgerly-yearend", "Year-end pack export with attached receipts", 1600, 1e3)
|
|
2616
|
+
}
|
|
2617
|
+
],
|
|
2618
|
+
autoplay: true,
|
|
2619
|
+
interval: 7,
|
|
2620
|
+
frame: "browser",
|
|
2621
|
+
theme: "light"
|
|
2622
|
+
},
|
|
2623
|
+
notes: {
|
|
2624
|
+
why: "This is the FIXED-SHAPE sibling of the core Tabs block: every tab has the same copy + media layout, so an array of objects gives marketers a predictable form and the converter a predictable target. Use the core slot-based Tabs only when tab panels hold arbitrary blocks. Media is a select + conditional image/video (exactly one required); the poster only shows for video. Auto-advance and its interval are design, and the interval is hidden until auto-advance is on.",
|
|
2625
|
+
sanity: "array of `tab` objects with a media union; `interval` hidden via `hidden: ({parent}) => !parent.autoplay`.",
|
|
2626
|
+
htmlSignals: [
|
|
2627
|
+
"`role=\"tablist\"` with `role=\"tab\"` buttons and `role=\"tabpanel\"` siblings, OR buttons with `aria-controls`/`data-tab` toggling panels",
|
|
2628
|
+
"Each panel contains the SAME structure: heading + paragraph + one large screenshot or video",
|
|
2629
|
+
"A progress bar inside the active tab button (auto-advance)",
|
|
2630
|
+
"Screenshots inside a browser-chrome frame (three dots, fake URL bar)"
|
|
2631
|
+
],
|
|
2632
|
+
mcp: "Tab button text → label (first line), secondary text inside the button → summary. Panel heading → title, paragraphs → body, panel button/link → button. Panel `<video>` → mediaType video (+ poster), `<img>` → screenshot. If tab buttons have an animated progress bar or the source JS rotates tabs with setInterval, set autoplay true and read the interval (ms → seconds). Browser/phone mockup chrome around media → frame. If panels differ structurally (one has a pricing table, one a form), use the core slot-based `tabs` instead.",
|
|
2633
|
+
pitfalls: ["Auto-advancing tabs must stop on interaction and respect reduced motion — the renderer handles it; never model a “pause” field.", "Tab labels longer than ~20 characters wrap on the top variant and break the underline."]
|
|
2634
|
+
}
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
id: "accordion-feature-image-swap",
|
|
2638
|
+
label: "Accordion features with image swap",
|
|
2639
|
+
proves: "One required image per item drives a shared media slot · open-state as design, not content",
|
|
2640
|
+
category: "Features & storytelling",
|
|
2641
|
+
tags: [
|
|
2642
|
+
"repeater",
|
|
2643
|
+
"media-heavy",
|
|
2644
|
+
"a11y",
|
|
2645
|
+
"variants",
|
|
2646
|
+
"rich-text"
|
|
2647
|
+
],
|
|
2648
|
+
brand: "Caldera",
|
|
2649
|
+
previewLayout: "accordion",
|
|
2650
|
+
schema: accordionFeatures,
|
|
2651
|
+
value: {
|
|
2652
|
+
_variant: "image-right",
|
|
2653
|
+
eyebrow: "The Ember Dutch Oven",
|
|
2654
|
+
heading: "One pot for bread, braises and the whole weekend.",
|
|
2655
|
+
items: [
|
|
2656
|
+
{
|
|
2657
|
+
_key: "a1",
|
|
2658
|
+
title: "A lid that doubles as a skillet",
|
|
2659
|
+
body: "<p>Flip the lid and it's a 10\" shallow pan with its own handles. Bake bread underneath, fry eggs on top.</p>",
|
|
2660
|
+
image: img("ember-lid-skillet", "Eggs frying in the inverted Dutch oven lid over coals", 1200, 1200)
|
|
2661
|
+
},
|
|
2662
|
+
{
|
|
2663
|
+
_key: "a2",
|
|
2664
|
+
title: "Legs for coals, flat for the stove",
|
|
2665
|
+
body: "<p>Removable feet lift the pot over coals and screw off for induction and gas.</p>",
|
|
2666
|
+
image: img("ember-legs", "Dutch oven standing on its feet over a bed of coals", 1200, 1200),
|
|
2667
|
+
link: {
|
|
2668
|
+
type: "page",
|
|
2669
|
+
href: "/journal/coal-cooking",
|
|
2670
|
+
label: "Coal cooking guide",
|
|
2671
|
+
title: "Coal cooking"
|
|
2672
|
+
}
|
|
2673
|
+
},
|
|
2674
|
+
{
|
|
2675
|
+
_key: "a3",
|
|
2676
|
+
title: "A rim that holds 18 briquettes",
|
|
2677
|
+
body: "<p>The raised lid rim keeps coals on top for even, oven-like heat — the way chuckwagon cooks did it.</p>",
|
|
2678
|
+
image: img("ember-briquettes", "Briquettes arranged on the Dutch oven lid rim", 1200, 1200)
|
|
2679
|
+
},
|
|
2680
|
+
{
|
|
2681
|
+
_key: "a4",
|
|
2682
|
+
title: "5.5 quarts, sized for six",
|
|
2683
|
+
body: "<p>Big enough for a whole chicken, small enough to carry from the car.</p>",
|
|
2684
|
+
image: img("ember-chicken", "A whole roast chicken in the Dutch oven", 1200, 1200)
|
|
2685
|
+
}
|
|
2686
|
+
],
|
|
2687
|
+
isFirstOpen: true,
|
|
2688
|
+
autoplay: false,
|
|
2689
|
+
theme: "light"
|
|
2690
|
+
},
|
|
2691
|
+
notes: {
|
|
2692
|
+
why: "Each accordion item OWNS the image shown while it is open, so the image lives on the item (required) rather than in a parallel array the editor would have to keep in sync. Which item starts open is a design toggle, not a per-item boolean, so there can never be two “default open” items. It is an array, not the FAQ block: these are product features with media, not questions, and must not emit FAQPage JSON-LD.",
|
|
2693
|
+
sanity: "array of `accordionFeature` objects {title, body (portable text), image, link}; openFirst as a boolean on the parent.",
|
|
2694
|
+
htmlSignals: [
|
|
2695
|
+
"`<details>/<summary>` or buttons with `aria-expanded` in one column, and ONE large image in the other column",
|
|
2696
|
+
"JS that swaps the image `src` (or toggles opacity on stacked images) when a row opens",
|
|
2697
|
+
"Several `<img>` stacked absolutely with only one visible, one per accordion row"
|
|
2698
|
+
],
|
|
2699
|
+
mcp: "Count accordion rows and images: if image count equals row count (stacked or swapped), map row i → image i. Summary/button text → title, revealed content → body (portable rich text, keep links), trailing link → link. If rows have NO images, this is an FAQ or plain accordion, not this entry. Image column side → variant. If the first row is expanded by default → isFirstOpen; a progress bar on the open row → autoplay.",
|
|
2700
|
+
pitfalls: ["Images of different aspect ratios cause the layout to jump on every click — warn when the source images differ.", "Do not tag these rows as FAQ structured data — they are not questions."]
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
id: "comparison-vs-competitors",
|
|
2705
|
+
label: "Comparison vs competitors",
|
|
2706
|
+
proves: "Brand columns + one `table` keyed by brand key (reorder-safe) · row groups · Yes / no with words · partial answers as Text cells · source in the table's own note + a required last-checked date",
|
|
2707
|
+
category: "Features & storytelling",
|
|
2708
|
+
tags: [
|
|
2709
|
+
"deep-nesting",
|
|
2710
|
+
"conditional",
|
|
2711
|
+
"repeater",
|
|
2712
|
+
"validation",
|
|
2713
|
+
"a11y",
|
|
2714
|
+
"variants"
|
|
2715
|
+
],
|
|
2716
|
+
brand: "Ledgerly",
|
|
2717
|
+
previewLayout: "table",
|
|
2718
|
+
schema: comparisonTable,
|
|
2719
|
+
value: {
|
|
2720
|
+
_variant: "table",
|
|
2721
|
+
eyebrow: "Compare",
|
|
2722
|
+
heading: "Built for small teams, priced like it.",
|
|
2723
|
+
intro: "How Ledgerly stacks up against the two tools most of our customers switched from.",
|
|
2724
|
+
columns: [
|
|
2725
|
+
{
|
|
2726
|
+
_key: "c1",
|
|
2727
|
+
key: "ledgerly",
|
|
2728
|
+
name: "Ledgerly",
|
|
2729
|
+
logo: {
|
|
2730
|
+
url: "https://cdn.ledgerly.com/brand/ledgerly-mark.svg",
|
|
2731
|
+
alt: "Ledgerly",
|
|
2732
|
+
name: "ledgerly-mark.svg",
|
|
2733
|
+
width: 96,
|
|
2734
|
+
height: 96
|
|
2735
|
+
},
|
|
2736
|
+
isHighlighted: true,
|
|
2737
|
+
badge: "Best value",
|
|
2738
|
+
price: "$29 / month",
|
|
2739
|
+
button: {
|
|
2740
|
+
label: "Start free trial",
|
|
2741
|
+
href: "/signup",
|
|
2742
|
+
type: "page",
|
|
2743
|
+
title: "Sign up",
|
|
2744
|
+
style: "primary"
|
|
2745
|
+
}
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
_key: "c2",
|
|
2749
|
+
key: "tallybook",
|
|
2750
|
+
name: "Tallybook",
|
|
2751
|
+
price: "$45 / month"
|
|
2752
|
+
},
|
|
2753
|
+
{
|
|
2754
|
+
_key: "c3",
|
|
2755
|
+
key: "sumwise",
|
|
2756
|
+
name: "Sumwise",
|
|
2757
|
+
price: "$38 / month"
|
|
2758
|
+
}
|
|
2759
|
+
],
|
|
2760
|
+
comparison: tableFrom("Ledgerly, Tallybook and Sumwise compared on bookkeeping and team features", [
|
|
2761
|
+
{
|
|
2762
|
+
_key: "capability",
|
|
2763
|
+
label: "Capability"
|
|
2764
|
+
},
|
|
2765
|
+
{
|
|
2766
|
+
_key: "ledgerly",
|
|
2767
|
+
label: "Ledgerly",
|
|
2768
|
+
type: "boolean",
|
|
2769
|
+
highlight: true
|
|
2770
|
+
},
|
|
2771
|
+
{
|
|
2772
|
+
_key: "tallybook",
|
|
2773
|
+
label: "Tallybook",
|
|
2774
|
+
type: "boolean"
|
|
2775
|
+
},
|
|
2776
|
+
{
|
|
2777
|
+
_key: "sumwise",
|
|
2778
|
+
label: "Sumwise",
|
|
2779
|
+
type: "boolean"
|
|
2780
|
+
}
|
|
2781
|
+
], [
|
|
2782
|
+
{
|
|
2783
|
+
cells: [
|
|
2784
|
+
{
|
|
2785
|
+
value: "Automatic bank reconciliation",
|
|
2786
|
+
note: "f1"
|
|
2787
|
+
},
|
|
2788
|
+
true,
|
|
2789
|
+
{
|
|
2790
|
+
value: "Partly: rules written by hand",
|
|
2791
|
+
type: "text"
|
|
2792
|
+
},
|
|
2793
|
+
true
|
|
2794
|
+
],
|
|
2795
|
+
group: "books"
|
|
2796
|
+
},
|
|
2797
|
+
{
|
|
2798
|
+
cells: [
|
|
2799
|
+
"Receipt capture by email",
|
|
2800
|
+
true,
|
|
2801
|
+
true,
|
|
2802
|
+
false
|
|
2803
|
+
],
|
|
2804
|
+
group: "books"
|
|
2805
|
+
},
|
|
2806
|
+
{
|
|
2807
|
+
cells: [
|
|
2808
|
+
"Multi-currency",
|
|
2809
|
+
"160 currencies",
|
|
2810
|
+
"Premium plan only",
|
|
2811
|
+
"12 currencies"
|
|
2812
|
+
],
|
|
2813
|
+
group: "books",
|
|
2814
|
+
type: "text"
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
cells: [
|
|
2818
|
+
"Users included",
|
|
2819
|
+
"Unlimited",
|
|
2820
|
+
"3",
|
|
2821
|
+
"5"
|
|
2822
|
+
],
|
|
2823
|
+
group: "team",
|
|
2824
|
+
type: "text"
|
|
2825
|
+
},
|
|
2826
|
+
{
|
|
2827
|
+
cells: [
|
|
2828
|
+
{
|
|
2829
|
+
value: "Bookkeeper access",
|
|
2830
|
+
note: "f2"
|
|
2831
|
+
},
|
|
2832
|
+
true,
|
|
2833
|
+
false,
|
|
2834
|
+
{
|
|
2835
|
+
value: "Read-only",
|
|
2836
|
+
type: "text"
|
|
2837
|
+
}
|
|
2838
|
+
],
|
|
2839
|
+
group: "team"
|
|
2840
|
+
}
|
|
2841
|
+
], {
|
|
2842
|
+
id: "tbl_ledgerly_compare",
|
|
2843
|
+
headerColumn: true,
|
|
2844
|
+
rowGroups: [{
|
|
2845
|
+
_key: "books",
|
|
2846
|
+
label: "Bookkeeping"
|
|
2847
|
+
}, {
|
|
2848
|
+
_key: "team",
|
|
2849
|
+
label: "Team"
|
|
2850
|
+
}],
|
|
2851
|
+
notes: "Based on each vendor's public pricing and help pages on 1 September 2026. Tallybook and Sumwise are trademarks of their owners.",
|
|
2852
|
+
footnotes: [{
|
|
2853
|
+
_key: "f1",
|
|
2854
|
+
text: "Matches bank lines to invoices and bills without manual rules."
|
|
2855
|
+
}, {
|
|
2856
|
+
_key: "f2",
|
|
2857
|
+
text: "Invite an outside bookkeeper without paying for a seat."
|
|
2858
|
+
}],
|
|
2859
|
+
design: {
|
|
2860
|
+
responsive: "scroll",
|
|
2861
|
+
sticky: true,
|
|
2862
|
+
hideCaption: true
|
|
2863
|
+
},
|
|
2864
|
+
jsonLd: {
|
|
2865
|
+
type: "ItemList",
|
|
2866
|
+
axis: "columns"
|
|
2867
|
+
}
|
|
2868
|
+
}),
|
|
2869
|
+
verifiedOn: "2026-09-01",
|
|
2870
|
+
theme: "light"
|
|
2871
|
+
},
|
|
2872
|
+
notes: {
|
|
2873
|
+
why: "A comparison table is 2-D data: brand COLUMNS (name, logo, badge, price, button: the header) + one `table` (CPO D42) whose brand columns are keyed by the same `key`, so adding or reordering a competitor never shifts a value into the wrong column. Rows sit in row groups (tbody + th scope=rowgroup) instead of 5 drill levels. Brand columns are Yes / no, written as ✓ plus the words 'Included' / 'Not included'; a row of words is a Text row and a partial answer is one Text cell ('Partly: rules written by hand'); explainers are footnotes, so their words stay on the page. The source line is the table's own note (rendered under the table and linked with aria-describedby), not a second block field; the last-checked date is a required field because comparative advertising claims go stale. If the same competitors appear on several pages, columns should become references to a `col_competitors` collection.",
|
|
2874
|
+
sanity: "`competitorComparison` with `columns: array<object{key, name, logo, isHighlighted}>` and rows of cells with a `column` string (validated by `Rule.custom`), or the v6.6 Portable Text table (rows › cells › blocks, `headerRows`), or `@sanity/table` (string cells). None keys a cell to its column.",
|
|
2875
|
+
htmlSignals: [
|
|
2876
|
+
"A `<table>` (or CSS grid) whose header row contains brand names/logos, with the first data column of capability names",
|
|
2877
|
+
"Cells containing check/cross SVGs, ✓ ✗ characters or `aria-label=\"Included\"`",
|
|
2878
|
+
"One header column visually highlighted (tinted background, “Recommended” badge)",
|
|
2879
|
+
"`<tbody>` sections or full-width rows acting as group headers (`colspan`)",
|
|
2880
|
+
"Small print below: “Compared on …”, “Based on public pricing”, trademark notice"
|
|
2881
|
+
],
|
|
2882
|
+
mcp: "Header cells → columns (slugify the name for `key`; img → logo; highlighted column → isHighlighted + badge text) AND the table's brand columns with `_key` = that key, type Yes / no. Body via the table detector (docs/TABLES.md): first cell → row header (tooltip → footnote); check icon/✓/“Yes”/aria-label included → true; cross/✗/“—”/“No” → false; “Limited”/“Partial” + words → a Text cell with those words; a row of words → row type Text. Full-width rows (colspan) start a row group. Small print under the table (source, trademark notice) → the table's `notes`; parse a date in it → verifiedOn, otherwise set verifiedOn to the import date and flag it for review. Never emit a separate `footnote` field on the block.",
|
|
2883
|
+
pitfalls: [
|
|
2884
|
+
"RESOLVED (D42): cells are keyed by column; never emit positional cells.",
|
|
2885
|
+
"Icons without text alternatives fail screen readers; the enum lets the renderer output “Included”/“Not included”.",
|
|
2886
|
+
"Comparative claims without a date and source are a legal risk — `verifiedOn` is required on purpose."
|
|
2887
|
+
]
|
|
2888
|
+
}
|
|
2889
|
+
},
|
|
2890
|
+
{
|
|
2891
|
+
id: "stats-band",
|
|
2892
|
+
label: "Stats band",
|
|
2893
|
+
proves: "Numbers stored as numbers (prefix/value/suffix) so they localise and count up · per-stat source + as-of date",
|
|
2894
|
+
category: "Features & storytelling",
|
|
2895
|
+
tags: [
|
|
2896
|
+
"repeater",
|
|
2897
|
+
"validation",
|
|
2898
|
+
"variants",
|
|
2899
|
+
"a11y",
|
|
2900
|
+
"localized"
|
|
2901
|
+
],
|
|
2902
|
+
brand: "Juniper Health",
|
|
2903
|
+
previewLayout: "stats",
|
|
2904
|
+
schema: statsBand,
|
|
2905
|
+
value: {
|
|
2906
|
+
_variant: "split",
|
|
2907
|
+
heading: "Care that shows up in the numbers.",
|
|
2908
|
+
stats: [
|
|
2909
|
+
{
|
|
2910
|
+
_key: "s1",
|
|
2911
|
+
value: 11,
|
|
2912
|
+
suffix: " min",
|
|
2913
|
+
label: "Median wait to see a clinician",
|
|
2914
|
+
source: {
|
|
2915
|
+
type: "url",
|
|
2916
|
+
href: "https://junipercare.com/reports/2026-access",
|
|
2917
|
+
label: "2026 access report",
|
|
2918
|
+
newTab: true
|
|
2919
|
+
},
|
|
2920
|
+
asOf: "2026-06-30"
|
|
2921
|
+
},
|
|
2922
|
+
{
|
|
2923
|
+
_key: "s2",
|
|
2924
|
+
value: 94,
|
|
2925
|
+
suffix: "%",
|
|
2926
|
+
label: "Patients who'd recommend us",
|
|
2927
|
+
source: {
|
|
2928
|
+
type: "url",
|
|
2929
|
+
href: "https://junipercare.com/reports/2026-access",
|
|
2930
|
+
label: "2026 access report",
|
|
2931
|
+
newTab: true
|
|
2932
|
+
},
|
|
2933
|
+
asOf: "2026-06-30"
|
|
2934
|
+
},
|
|
2935
|
+
{
|
|
2936
|
+
_key: "s3",
|
|
2937
|
+
value: 1.2,
|
|
2938
|
+
suffix: "M",
|
|
2939
|
+
decimals: 1,
|
|
2940
|
+
label: "Visits completed since 2021",
|
|
2941
|
+
asOf: "2026-09-01"
|
|
2942
|
+
},
|
|
2943
|
+
{
|
|
2944
|
+
_key: "s4",
|
|
2945
|
+
value: 42,
|
|
2946
|
+
label: "States with licensed clinicians",
|
|
2947
|
+
asOf: "2026-09-01"
|
|
2948
|
+
}
|
|
2949
|
+
],
|
|
2950
|
+
useCountUp: true,
|
|
2951
|
+
theme: "brand"
|
|
2952
|
+
},
|
|
2953
|
+
notes: {
|
|
2954
|
+
why: "A stat is split into prefix · number · suffix so the number is real data: it can animate, be formatted per locale (1.2M vs 1,2 M) and be checked. The core `statBlock` stores a display string (\"120k+\") for quick bento cells — this section is for numbers that matter and need a SOURCE and AS-OF date (health, finance and B2B claims get audited). Count-up is design; the renderer skips it under reduced motion and always outputs the final number in the HTML.",
|
|
2955
|
+
sanity: "array of `stat` objects {prefix: string, value: number, suffix: string, label, source: url}; Sanity users often store `value` as a string, losing formatting.",
|
|
2956
|
+
htmlSignals: [
|
|
2957
|
+
"A row of 2–5 items each with a very large number (font-size ≥ 2.5rem) and a small label beneath",
|
|
2958
|
+
"`data-count`, `data-target`, `countup`, `odometer` attributes or classes",
|
|
2959
|
+
"Numbers with units/magnitudes: “94%”, “$2.4B”, “11 min”, “10k+”",
|
|
2960
|
+
"Footnote markers (*, †, superscript numbers) linking to a sources line"
|
|
2961
|
+
],
|
|
2962
|
+
mcp: "Parse each big number with a regex: leading non-digits → prefix, the number (strip thousands separators; respect the page locale's decimal mark) → value, trailing text → suffix (keep the leading space if present), digits after the decimal → decimals. Prefer the `data-target`/`data-count` attribute over the visible text when both exist (the visible text may be mid-animation “0”). Label → label. Footnote text with a URL → source; any date in it → asOf. Presence of count-up attributes → useCountUp true.",
|
|
2963
|
+
pitfalls: ["Scraping during a count-up animation captures “0” — read the data attribute or wait for the final value.", "“1.2M” is value 1.2 + suffix M, not 1200000 with no suffix, unless the design formats compact numbers itself."]
|
|
2964
|
+
}
|
|
2965
|
+
},
|
|
2966
|
+
{
|
|
2967
|
+
id: "manifesto-pull-quote",
|
|
2968
|
+
label: "Manifesto / pull quote",
|
|
2969
|
+
proves: "Mode select swaps an array of rich-text lines for a single quote · overlay shown only when a background exists (notEmpty)",
|
|
2970
|
+
category: "Features & storytelling",
|
|
2971
|
+
tags: [
|
|
2972
|
+
"conditional",
|
|
2973
|
+
"rich-text",
|
|
2974
|
+
"variants",
|
|
2975
|
+
"localized"
|
|
2976
|
+
],
|
|
2977
|
+
brand: "Caldera",
|
|
2978
|
+
previewLayout: "centered",
|
|
2979
|
+
schema: manifesto,
|
|
2980
|
+
value: {
|
|
2981
|
+
_variant: "lines",
|
|
2982
|
+
eyebrow: "What we believe",
|
|
2983
|
+
mode: "lines",
|
|
2984
|
+
lines: [
|
|
2985
|
+
"<p>Buy it <em>once</em>.</p>",
|
|
2986
|
+
"<p>Cook outside more than you scroll.</p>",
|
|
2987
|
+
"<p>Fix things before you replace them.</p>",
|
|
2988
|
+
"<p>Make it in a place you can <em>visit</em>.</p>",
|
|
2989
|
+
"<p>Hand it down with the stains still on it.</p>"
|
|
2990
|
+
],
|
|
2991
|
+
attribution: {
|
|
2992
|
+
name: "Ana Ruiz",
|
|
2993
|
+
role: "Founder, Caldera Cookware",
|
|
2994
|
+
signature: {
|
|
2995
|
+
url: "https://cdn.caldera.co/brand/ana-signature.svg",
|
|
2996
|
+
alt: "Ana Ruiz's signature",
|
|
2997
|
+
name: "ana-signature.svg",
|
|
2998
|
+
width: 320,
|
|
2999
|
+
height: 120
|
|
3000
|
+
}
|
|
3001
|
+
},
|
|
3002
|
+
background: img("caldera-embers-night", "Embers glowing in a fire ring at night", 2400, 1400, {
|
|
3003
|
+
x: 50,
|
|
3004
|
+
y: 60
|
|
3005
|
+
}),
|
|
3006
|
+
overlay: 50,
|
|
3007
|
+
size: "display",
|
|
3008
|
+
theme: "dark"
|
|
3009
|
+
},
|
|
3010
|
+
notes: {
|
|
3011
|
+
why: "One block covers both a multi-line manifesto and a single pull quote because they share attribution and treatment; a `mode` select decides which content field shows so editors never see both. Each manifesto line is a tiny RICH-TEXT value (italic for emphasis only) rather than a plain string with markup, so emphasis survives translation. Attribution is a small inline object. The overlay slider only appears when a background image exists (`notEmpty` condition).",
|
|
3012
|
+
sanity: "object with `mode: string`, `lines: array<block>` (portable text restricted to `em`), `quote: text`, `attribution: object`.",
|
|
3013
|
+
htmlSignals: [
|
|
3014
|
+
"Very large type (≥ 2.5rem) over a full-width section with a few short lines or one `<blockquote>`",
|
|
3015
|
+
"`<blockquote>` with `<cite>` or a `<figcaption>` holding name/role, often with a signature `<img>`",
|
|
3016
|
+
"Lines revealed one at a time on scroll (`data-aos`, GSAP `SplitText`, staggered opacity)"
|
|
3017
|
+
],
|
|
3018
|
+
mcp: "One `<blockquote>` (or one paragraph in display type) → mode quote, quote text WITHOUT surrounding quotation marks (the renderer adds typographic quotes). Several short display-size lines/paragraphs → mode lines, one line per element, keeping `<em>`/`<i>` as italic marks and dropping spans used only for color. `<cite>`/caption → attribution.name / role; signature image → attribution.signature. Section background-image → background + overlay from the gradient/scrim opacity.",
|
|
3019
|
+
pitfalls: ["Quotation marks typed into content double up with the design's quotes — strip them.", "Lines split by `<br>` inside one paragraph are still separate lines."]
|
|
3020
|
+
}
|
|
3021
|
+
},
|
|
3022
|
+
{
|
|
3023
|
+
id: "problem-solution",
|
|
3024
|
+
label: "Problem → solution",
|
|
3025
|
+
proves: "Pairs keep both columns aligned · column labels as small objects with defaults",
|
|
3026
|
+
category: "Features & storytelling",
|
|
3027
|
+
tags: [
|
|
3028
|
+
"repeater",
|
|
3029
|
+
"variants",
|
|
3030
|
+
"validation"
|
|
3031
|
+
],
|
|
3032
|
+
brand: "Ledgerly",
|
|
3033
|
+
previewLayout: "grid-2",
|
|
3034
|
+
schema: problemSolution,
|
|
3035
|
+
value: {
|
|
3036
|
+
_variant: "pairs",
|
|
3037
|
+
eyebrow: "Sound familiar?",
|
|
3038
|
+
heading: "Month-end shouldn't take your whole week.",
|
|
3039
|
+
problem: {
|
|
3040
|
+
label: "Without Ledgerly",
|
|
3041
|
+
summary: "The average small business owner spends 11 hours a month on books."
|
|
3042
|
+
},
|
|
3043
|
+
solution: {
|
|
3044
|
+
label: "With Ledgerly",
|
|
3045
|
+
summary: "Most customers close the month in under two hours.",
|
|
3046
|
+
button: {
|
|
3047
|
+
label: "See how it works",
|
|
3048
|
+
href: "/how-it-works",
|
|
3049
|
+
type: "page",
|
|
3050
|
+
title: "How it works",
|
|
3051
|
+
style: "primary",
|
|
3052
|
+
arrow: true
|
|
3053
|
+
}
|
|
3054
|
+
},
|
|
3055
|
+
pairs: [
|
|
3056
|
+
{
|
|
3057
|
+
_key: "p1",
|
|
3058
|
+
problem: "Matching bank lines to invoices by hand, one by one.",
|
|
3059
|
+
solution: "95% of bank lines match themselves overnight.",
|
|
3060
|
+
metric: "6 hours saved a month"
|
|
3061
|
+
},
|
|
3062
|
+
{
|
|
3063
|
+
_key: "p2",
|
|
3064
|
+
problem: "Chasing clients for payment over awkward emails.",
|
|
3065
|
+
solution: "Polite reminders go out on schedule, in your voice.",
|
|
3066
|
+
metric: "Paid 9 days sooner"
|
|
3067
|
+
},
|
|
3068
|
+
{
|
|
3069
|
+
_key: "p3",
|
|
3070
|
+
problem: "A shoebox of receipts every April.",
|
|
3071
|
+
solution: "Forward a receipt once; it's filed against the right expense.",
|
|
3072
|
+
metric: ""
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
_key: "p4",
|
|
3076
|
+
problem: "Not knowing if you can afford the next hire.",
|
|
3077
|
+
solution: "A 90-day cash forecast that updates every morning.",
|
|
3078
|
+
metric: ""
|
|
3079
|
+
}
|
|
3080
|
+
],
|
|
3081
|
+
tone: "muted",
|
|
3082
|
+
theme: "light"
|
|
3083
|
+
},
|
|
3084
|
+
notes: {
|
|
3085
|
+
why: "Problems and solutions are stored as PAIRS, not two independent arrays, because the strongest version of this section lines each pain up with its fix — two arrays drift (four problems, three solutions) and the paired layout breaks. The two-column variant reads the same data column-wise. Column labels and summaries are small objects with sensible defaults so the common case needs no typing.",
|
|
3086
|
+
sanity: "`problemSolution` with `pairs: array<object{problem, solution, metric}>` plus two small objects.",
|
|
3087
|
+
htmlSignals: [
|
|
3088
|
+
"Two columns labeled “Before / After”, “Without X / With X”, “The old way / The new way”",
|
|
3089
|
+
"One column with ✗ / red / strikethrough items, the other with ✓ / green items",
|
|
3090
|
+
"Equal item counts in both columns, often vertically aligned"
|
|
3091
|
+
],
|
|
3092
|
+
mcp: "Find the two lists; take their headers → problem.label / solution.label. If counts match, zip item i of each list into a pair; if they differ, create pairs for the shorter length and append the leftovers with an empty partner and a review flag. Small numeric callouts (“6 hours saved”) → metric. Strike-through/red styling → tone warning. Layout: rows where each problem sits next to its fix → pairs, two independent stacks → columns.",
|
|
3093
|
+
pitfalls: ["Unequal lists in the source must not be silently truncated — flag them."]
|
|
3094
|
+
}
|
|
3095
|
+
},
|
|
3096
|
+
{
|
|
3097
|
+
id: "integrations-grid",
|
|
3098
|
+
label: "Integrations grid",
|
|
3099
|
+
proves: "Dynamic query vs manual references to a collection · filter chips derived from referenced categories",
|
|
3100
|
+
category: "Features & storytelling",
|
|
3101
|
+
tags: [
|
|
3102
|
+
"dynamic-query",
|
|
3103
|
+
"references",
|
|
3104
|
+
"conditional",
|
|
3105
|
+
"variants",
|
|
3106
|
+
"media-heavy"
|
|
3107
|
+
],
|
|
3108
|
+
brand: "Ledgerly",
|
|
3109
|
+
previewLayout: "grid-4",
|
|
3110
|
+
schema: integrations,
|
|
3111
|
+
value: {
|
|
3112
|
+
_variant: "grid",
|
|
3113
|
+
eyebrow: "Integrations",
|
|
3114
|
+
heading: "Plays well with the tools you already pay for.",
|
|
3115
|
+
subheading: "Connect your bank, payroll, payments and store in a couple of clicks. No CSVs.",
|
|
3116
|
+
source: "query",
|
|
3117
|
+
categories: [
|
|
3118
|
+
"ic_banking",
|
|
3119
|
+
"ic_payments",
|
|
3120
|
+
"ic_payroll",
|
|
3121
|
+
"ic_commerce"
|
|
3122
|
+
],
|
|
3123
|
+
showFeaturedOnly: false,
|
|
3124
|
+
limit: 24,
|
|
3125
|
+
sort: "popular",
|
|
3126
|
+
items: [],
|
|
3127
|
+
showFilter: true,
|
|
3128
|
+
showSearch: false,
|
|
3129
|
+
viewAllButton: {
|
|
3130
|
+
label: "Browse all 140 integrations",
|
|
3131
|
+
href: "/integrations",
|
|
3132
|
+
type: "page",
|
|
3133
|
+
title: "Integrations",
|
|
3134
|
+
style: "outline",
|
|
3135
|
+
arrow: true
|
|
3136
|
+
},
|
|
3137
|
+
columns: 4,
|
|
3138
|
+
isMonochrome: false,
|
|
3139
|
+
theme: "light"
|
|
3140
|
+
},
|
|
3141
|
+
notes: {
|
|
3142
|
+
why: "Integrations are REFERENCED, not inlined: each one has its own detail page (/integrations/stripe), logo, category and description, and appears on several pages — that is a collection (`col_integrations`) with a category reference (`col_integration_categories`). The section stores only HOW to pick: a dynamic query (categories, featured, limit, sort) so new integrations appear automatically, or a manual list of references for a curated row. Filter chips are derived from the categories of what is shown, so there is no second list to maintain. Inline the items only when the site has no integration pages and fewer than ~12 logos.",
|
|
3143
|
+
sanity: "`integrationsGrid` with `mode: string`, `categories: array<reference to integrationCategory>`, `items: array<reference to integration>`; the query runs in GROQ: `*[_type=='integration' && category._ref in $cats] | order(popularity desc)[0...$limit]`.",
|
|
3144
|
+
htmlSignals: [
|
|
3145
|
+
"A grid of 12+ tiles each with a third-party logo, a product name and a short line, linking to `/integrations/<slug>`",
|
|
3146
|
+
"Filter chips or a sidebar of categories (“All”, “Payments”, “CRM”) with `data-filter`/`aria-pressed`",
|
|
3147
|
+
"A search input filtering the tiles; a “See all integrations” link"
|
|
3148
|
+
],
|
|
3149
|
+
mcp: "If tiles link to per-integration pages (shared `/integrations/` prefix), create or match `col_integrations` entries (name, slug from href, logo, one-line description, category from the active filter or a `data-category` attribute) and set source to `query` with the categories present. If tiles do not link anywhere, use source `manual` and create entries anyway so logos are reusable. Filter chip labels → integration category entries (skip “All”). A visible search box → showSearch. Count tiles shown before a “See all” link → limit.",
|
|
3150
|
+
pitfalls: ["Brand logos have usage rules — keep the original SVG, never recolor it in content; `isMonochrome` is a render-time filter.", "Category options cannot come from a sibling array in our schema, which is exactly why categories are a referenced collection."]
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
];
|
|
3154
|
+
//#endregion
|
|
3155
|
+
export { ENTRIES };
|
|
3156
|
+
|
|
3157
|
+
//# sourceMappingURL=features.entries.js.map
|