@bettercms-ai/kit 0.1.1 → 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 +22 -16
- package/dist/_virtual/_rolldown/runtime.js +13 -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/dist/adapters/icons.d.ts +15 -0
- package/dist/adapters/icons.js +27 -0
- package/dist/adapters/icons.js.map +1 -0
- package/dist/adapters/lossy.d.ts +326 -0
- package/dist/adapters/lossy.js +414 -0
- package/dist/adapters/lossy.js.map +1 -0
- package/dist/adapters/prod-types.d.ts +546 -0
- package/dist/adapters/prod-types.js +714 -0
- package/dist/adapters/prod-types.js.map +1 -0
- package/dist/adapters/schema.d.ts +154 -0
- package/dist/adapters/schema.js +3020 -0
- package/dist/adapters/schema.js.map +1 -0
- package/dist/adapters/values.d.ts +221 -0
- package/dist/adapters/values.js +623 -0
- package/dist/adapters/values.js.map +1 -0
- package/dist/brand/logo.d.ts +17 -0
- package/dist/brand/logo.js +96 -0
- package/dist/brand/logo.js.map +1 -0
- package/dist/context.d.ts +359 -0
- package/dist/context.js +217 -0
- package/dist/context.js.map +1 -0
- package/dist/controls/button.d.ts +72 -0
- package/dist/controls/button.js +578 -0
- package/dist/controls/button.js.map +1 -0
- package/dist/controls/c1.d.ts +11 -0
- package/dist/controls/c1.js +24 -0
- package/dist/controls/c1.js.map +1 -0
- package/dist/controls/c2.d.ts +10 -0
- package/dist/controls/c2.js +19 -0
- package/dist/controls/c2.js.map +1 -0
- package/dist/controls/choice.d.ts +44 -0
- package/dist/controls/choice.js +463 -0
- package/dist/controls/choice.js.map +1 -0
- package/dist/controls/code-register.d.ts +1 -0
- package/dist/controls/code-register.js +8 -0
- package/dist/controls/code-register.js.map +1 -0
- package/dist/controls/code.d.ts +42 -0
- package/dist/controls/code.js +397 -0
- package/dist/controls/code.js.map +1 -0
- package/dist/controls/color.d.ts +5 -0
- package/dist/controls/color.js +130 -0
- package/dist/controls/color.js.map +1 -0
- package/dist/controls/component-services.d.ts +156 -0
- package/dist/controls/component-services.js +7 -0
- package/dist/controls/component-services.js.map +1 -0
- package/dist/controls/date-register.d.ts +1 -0
- package/dist/controls/date-register.js +9 -0
- package/dist/controls/date-register.js.map +1 -0
- package/dist/controls/date-zone.d.ts +104 -0
- package/dist/controls/date-zone.js +278 -0
- package/dist/controls/date-zone.js.map +1 -0
- package/dist/controls/date.d.ts +7 -0
- package/dist/controls/date.js +466 -0
- package/dist/controls/date.js.map +1 -0
- package/dist/controls/form-picker-register.d.ts +1 -0
- package/dist/controls/form-picker-register.js +19 -0
- package/dist/controls/form-picker-register.js.map +1 -0
- package/dist/controls/form-picker.d.ts +43 -0
- package/dist/controls/form-picker.js +85 -0
- package/dist/controls/form-picker.js.map +1 -0
- package/dist/controls/icon-register.d.ts +1 -0
- package/dist/controls/icon-register.js +8 -0
- package/dist/controls/icon-register.js.map +1 -0
- package/dist/controls/icon.d.ts +40 -0
- package/dist/controls/icon.js +360 -0
- package/dist/controls/icon.js.map +1 -0
- package/dist/controls/index.d.ts +5 -0
- package/dist/controls/index.js +38 -0
- package/dist/controls/index.js.map +1 -0
- package/dist/controls/json-register.d.ts +1 -0
- package/dist/controls/json-register.js +8 -0
- package/dist/controls/json-register.js.map +1 -0
- package/dist/controls/json.d.ts +2 -0
- package/dist/controls/json.js +82 -0
- package/dist/controls/json.js.map +1 -0
- package/dist/controls/link.d.ts +260 -0
- package/dist/controls/link.js +1186 -0
- package/dist/controls/link.js.map +1 -0
- package/dist/controls/location-register.d.ts +1 -0
- package/dist/controls/location-register.js +8 -0
- package/dist/controls/location-register.js.map +1 -0
- package/dist/controls/location.d.ts +16 -0
- package/dist/controls/location.js +221 -0
- package/dist/controls/location.js.map +1 -0
- package/dist/controls/media-register.d.ts +1 -0
- package/dist/controls/media-register.js +12 -0
- package/dist/controls/media-register.js.map +1 -0
- package/dist/controls/media-services.d.ts +46 -0
- package/dist/controls/media-services.js +0 -0
- package/dist/controls/media.d.ts +104 -0
- package/dist/controls/media.js +1396 -0
- package/dist/controls/media.js.map +1 -0
- package/dist/controls/number.d.ts +22 -0
- package/dist/controls/number.js +196 -0
- package/dist/controls/number.js.map +1 -0
- package/dist/controls/reference-register.d.ts +1 -0
- package/dist/controls/reference-register.js +10 -0
- package/dist/controls/reference-register.js.map +1 -0
- package/dist/controls/reference-services.d.ts +58 -0
- package/dist/controls/reference-services.js +0 -0
- package/dist/controls/reference.d.ts +23 -0
- package/dist/controls/reference.js +777 -0
- package/dist/controls/reference.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +49 -0
- package/dist/controls/rich-text/blocks.js +1859 -0
- package/dist/controls/rich-text/blocks.js.map +1 -0
- package/dist/controls/rich-text/catalog.d.ts +101 -0
- package/dist/controls/rich-text/catalog.js +623 -0
- package/dist/controls/rich-text/catalog.js.map +1 -0
- package/dist/controls/rich-text/codec.d.ts +64 -0
- package/dist/controls/rich-text/codec.js +34 -0
- package/dist/controls/rich-text/codec.js.map +1 -0
- package/dist/controls/rich-text/component-card.d.ts +14 -0
- package/dist/controls/rich-text/component-card.js +1024 -0
- 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/dist/controls/rich-text/control.d.ts +20 -0
- package/dist/controls/rich-text/control.js +296 -0
- package/dist/controls/rich-text/control.js.map +1 -0
- package/dist/controls/rich-text/dom.d.ts +233 -0
- package/dist/controls/rich-text/dom.js +1019 -0
- package/dist/controls/rich-text/dom.js.map +1 -0
- package/dist/controls/rich-text/editor.d.ts +57 -0
- package/dist/controls/rich-text/editor.js +1630 -0
- package/dist/controls/rich-text/editor.js.map +1 -0
- package/dist/controls/rich-text/floating.d.ts +28 -0
- package/dist/controls/rich-text/floating.js +154 -0
- package/dist/controls/rich-text/floating.js.map +1 -0
- package/dist/controls/rich-text/index.d.ts +1 -0
- package/dist/controls/rich-text/index.js +12 -0
- package/dist/controls/rich-text/index.js.map +1 -0
- package/dist/controls/rich-text/link-form.d.ts +13 -0
- package/dist/controls/rich-text/link-form.js +147 -0
- package/dist/controls/rich-text/link-form.js.map +1 -0
- package/dist/controls/rich-text/markdown.d.ts +6 -0
- package/dist/controls/rich-text/markdown.js +181 -0
- package/dist/controls/rich-text/markdown.js.map +1 -0
- package/dist/controls/rich-text/ops.d.ts +35 -0
- package/dist/controls/rich-text/ops.js +316 -0
- 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/dist/controls/rich-text/toolbar.d.ts +89 -0
- package/dist/controls/rich-text/toolbar.js +343 -0
- package/dist/controls/rich-text/toolbar.js.map +1 -0
- package/dist/controls/scale.d.ts +56 -0
- package/dist/controls/scale.js +563 -0
- package/dist/controls/scale.js.map +1 -0
- package/dist/controls/slot-register.d.ts +1 -0
- package/dist/controls/slot-register.js +16 -0
- package/dist/controls/slot-register.js.map +1 -0
- package/dist/controls/slot-services.d.ts +122 -0
- package/dist/controls/slot-services.js +155 -0
- package/dist/controls/slot-services.js.map +1 -0
- package/dist/controls/slot.d.ts +33 -0
- package/dist/controls/slot.js +366 -0
- package/dist/controls/slot.js.map +1 -0
- package/dist/controls/table/index.d.ts +1 -0
- package/dist/controls/table/index.js +12 -0
- package/dist/controls/table/index.js.map +1 -0
- package/dist/controls/table/prod.d.ts +21 -0
- package/dist/controls/table/prod.js +79 -0
- package/dist/controls/table/prod.js.map +1 -0
- package/dist/controls/table/refs.d.ts +50 -0
- package/dist/controls/table/refs.js +79 -0
- package/dist/controls/table/refs.js.map +1 -0
- package/dist/controls/table/stored.d.ts +15 -0
- package/dist/controls/table/stored.js +44 -0
- package/dist/controls/table/stored.js.map +1 -0
- package/dist/controls/table/table-editor.d.ts +103 -0
- package/dist/controls/table/table-editor.js +2725 -0
- package/dist/controls/table/table-editor.js.map +1 -0
- package/dist/controls/table/table-link.d.ts +24 -0
- package/dist/controls/table/table-link.js +180 -0
- package/dist/controls/table/table-link.js.map +1 -0
- package/dist/controls/table/table.d.ts +9 -0
- package/dist/controls/table/table.js +103 -0
- package/dist/controls/table/table.js.map +1 -0
- package/dist/controls/text.d.ts +17 -0
- package/dist/controls/text.js +322 -0
- 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/dist/engine/array.d.ts +101 -0
- package/dist/engine/array.js +906 -0
- package/dist/engine/array.js.map +1 -0
- package/dist/engine/bits.d.ts +24 -0
- package/dist/engine/bits.js +60 -0
- package/dist/engine/bits.js.map +1 -0
- package/dist/engine/breadcrumb.d.ts +45 -0
- package/dist/engine/breadcrumb.js +63 -0
- package/dist/engine/breadcrumb.js.map +1 -0
- package/dist/engine/expand.d.ts +66 -0
- package/dist/engine/expand.js +153 -0
- package/dist/engine/expand.js.map +1 -0
- package/dist/engine/field-list.d.ts +20 -0
- package/dist/engine/field-list.js +19 -0
- package/dist/engine/field-list.js.map +1 -0
- package/dist/engine/field-shell.d.ts +49 -0
- package/dist/engine/field-shell.js +988 -0
- package/dist/engine/field-shell.js.map +1 -0
- package/dist/engine/frame.d.ts +122 -0
- package/dist/engine/frame.js +601 -0
- package/dist/engine/frame.js.map +1 -0
- package/dist/engine/group-summary.d.ts +5 -0
- package/dist/engine/group-summary.js +70 -0
- package/dist/engine/group-summary.js.map +1 -0
- package/dist/engine/grouping.d.ts +238 -0
- package/dist/engine/grouping.js +996 -0
- package/dist/engine/grouping.js.map +1 -0
- package/dist/engine/item-list.d.ts +99 -0
- package/dist/engine/item-list.js +725 -0
- package/dist/engine/item-list.js.map +1 -0
- package/dist/engine/kit-field.d.ts +72 -0
- package/dist/engine/kit-field.js +558 -0
- package/dist/engine/kit-field.js.map +1 -0
- package/dist/engine/schema-form.d.ts +116 -0
- package/dist/engine/schema-form.js +462 -0
- package/dist/engine/schema-form.js.map +1 -0
- package/dist/engine/slot.d.ts +13 -0
- package/dist/engine/slot.js +216 -0
- package/dist/engine/slot.js.map +1 -0
- package/dist/index.d.ts +45 -3139
- package/dist/index.js +47 -2036
- package/dist/index.js.map +1 -0
- package/dist/kit.css +75 -0
- package/dist/lib/cn.d.ts +4 -0
- package/dist/lib/cn.js +56 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/columns.d.ts +14 -0
- package/dist/lib/columns.js +30 -0
- package/dist/lib/columns.js.map +1 -0
- package/dist/lib/frames.d.ts +36 -0
- package/dist/lib/frames.js +214 -0
- 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/dist/lib/icons.d.ts +16 -0
- package/dist/lib/icons.js +173 -0
- 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/dist/lib/path.d.ts +29 -0
- package/dist/lib/path.js +66 -0
- package/dist/lib/path.js.map +1 -0
- package/dist/lib/schema.d.ts +80 -0
- package/dist/lib/schema.js +384 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/utils.d.ts +7 -0
- package/dist/lib/utils.js +37 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/validate.d.ts +30 -0
- package/dist/lib/validate.js +224 -0
- 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/dist/schema/adapters.d.ts +28 -0
- package/dist/schema/adapters.js +492 -0
- package/dist/schema/adapters.js.map +1 -0
- package/dist/schema/help.d.ts +27 -0
- package/dist/schema/help.js +56 -0
- package/dist/schema/help.js.map +1 -0
- package/dist/schema/scale.d.ts +69 -0
- package/dist/schema/scale.js +250 -0
- package/dist/schema/scale.js.map +1 -0
- package/dist/schema/sources.d.ts +103 -0
- package/dist/schema/sources.js +65 -0
- package/dist/schema/sources.js.map +1 -0
- package/dist/schema/table.d.ts +319 -0
- package/dist/schema/table.js +1199 -0
- package/dist/schema/table.js.map +1 -0
- package/dist/schema/types.d.ts +297 -0
- package/dist/schema/types.js +14 -0
- 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 +20 -1504
- package/dist/theme.js +43 -4126
- package/dist/theme.js.map +1 -0
- package/dist/tokens.css +542 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +0 -0
- package/dist/ui/action-tray.d.ts +54 -0
- package/dist/ui/action-tray.js +154 -0
- 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/dist/ui/button.d.ts +57 -0
- package/dist/ui/button.js +201 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/choice-switch.d.ts +36 -0
- package/dist/ui/choice-switch.js +57 -0
- package/dist/ui/choice-switch.js.map +1 -0
- package/dist/ui/controls.d.ts +83 -0
- package/dist/ui/controls.js +331 -0
- package/dist/ui/controls.js.map +1 -0
- package/dist/ui/display.d.ts +163 -0
- package/dist/ui/display.js +455 -0
- package/dist/ui/display.js.map +1 -0
- package/dist/ui/expanded-editor-modal.d.ts +26 -0
- package/dist/ui/expanded-editor-modal.js +140 -0
- package/dist/ui/expanded-editor-modal.js.map +1 -0
- package/dist/ui/field-context.d.ts +12 -0
- package/dist/ui/field-context.js +23 -0
- package/dist/ui/field-context.js.map +1 -0
- package/dist/ui/floating-panel.d.ts +152 -0
- package/dist/ui/floating-panel.js +928 -0
- package/dist/ui/floating-panel.js.map +1 -0
- package/dist/ui/index.d.ts +16 -0
- package/dist/ui/index.js +18 -0
- package/dist/ui/input.d.ts +78 -0
- package/dist/ui/input.js +250 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/mode-bar.d.ts +31 -0
- package/dist/ui/mode-bar.js +65 -0
- package/dist/ui/mode-bar.js.map +1 -0
- package/dist/ui/overlay.d.ts +195 -0
- package/dist/ui/overlay.js +697 -0
- package/dist/ui/overlay.js.map +1 -0
- package/dist/ui/path-crumbs.d.ts +90 -0
- package/dist/ui/path-crumbs.js +566 -0
- package/dist/ui/path-crumbs.js.map +1 -0
- package/dist/ui/presence.d.ts +16 -0
- package/dist/ui/presence.js +65 -0
- package/dist/ui/presence.js.map +1 -0
- package/dist/ui/select.d.ts +66 -0
- package/dist/ui/select.js +407 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui/shortcut-hint.d.ts +14 -0
- package/dist/ui/shortcut-hint.js +35 -0
- 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/dist/ui/surfaces.d.ts +23 -0
- package/dist/ui/surfaces.js +26 -0
- 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/dist/ui/tooltip-root.d.ts +13 -0
- package/dist/ui/tooltip-root.js +77 -0
- package/dist/ui/tooltip-root.js.map +1 -0
- package/dist/ui/variants.d.ts +15 -0
- package/dist/ui/variants.js +14 -0
- package/dist/ui/variants.js.map +1 -0
- package/package.json +76 -50
- package/LICENSE +0 -21
- package/dist/_chunks-cjs/_visual-editing.js +0 -5898
- package/dist/_chunks-cjs/refractor.js +0 -22
- package/dist/_chunks-es/_visual-editing.mjs +0 -5904
- package/dist/_chunks-es/refractor.mjs +0 -25
- package/dist/_color.d.mts +0 -119
- package/dist/_color.d.ts +0 -119
- package/dist/_color.js +0 -1412
- package/dist/_color.mjs +0 -1411
- package/dist/_visual-editing.d.mts +0 -1126
- package/dist/_visual-editing.d.ts +0 -1126
- package/dist/_visual-editing.js +0 -30
- package/dist/_visual-editing.mjs +0 -30
- package/dist/index.d.mts +0 -3140
- package/dist/index.mjs +0 -2044
- package/dist/theme.d.mts +0 -1506
- package/dist/theme.mjs +0 -4126
- package/exports/_visual-editing.ts +0 -31
- package/exports/index.ts +0 -1
- package/exports/theme.ts +0 -1
- package/src/color/index.d.mts +0 -119
- package/src/color/index.mjs +0 -1411
- package/src/core/__workshop__/constants.ts +0 -293
- package/src/core/_compat.ts +0 -264
- package/src/core/components/autocomplete/__mocks__/apiStore.ts +0 -49
- package/src/core/components/autocomplete/__mocks__/countries.ts +0 -245
- package/src/core/components/autocomplete/__workshop__/async.tsx +0 -156
- package/src/core/components/autocomplete/__workshop__/constrainedHeight.tsx +0 -137
- package/src/core/components/autocomplete/__workshop__/custom.tsx +0 -89
- package/src/core/components/autocomplete/__workshop__/example.tsx +0 -79
- package/src/core/components/autocomplete/__workshop__/focusAndBlur.tsx +0 -36
- package/src/core/components/autocomplete/__workshop__/fullscreen.tsx +0 -294
- package/src/core/components/autocomplete/__workshop__/index.ts +0 -39
- package/src/core/components/autocomplete/__workshop__/types.ts +0 -4
- package/src/core/components/autocomplete/autocomplete.styles.tsx +0 -39
- package/src/core/components/autocomplete/autocomplete.tsx +0 -767
- package/src/core/components/autocomplete/autocompleteOption.tsx +0 -45
- package/src/core/components/autocomplete/autocompleteReducer.ts +0 -47
- package/src/core/components/autocomplete/constants.ts +0 -28
- package/src/core/components/autocomplete/index.ts +0 -2
- package/src/core/components/autocomplete/types.ts +0 -108
- package/src/core/components/breadcrumbs/__workshop__/example.tsx +0 -62
- package/src/core/components/breadcrumbs/__workshop__/index.ts +0 -14
- package/src/core/components/breadcrumbs/breadcrumbs.styles.ts +0 -18
- package/src/core/components/breadcrumbs/breadcrumbs.test.tsx +0 -51
- package/src/core/components/breadcrumbs/breadcrumbs.tsx +0 -133
- package/src/core/components/breadcrumbs/index.ts +0 -1
- package/src/core/components/dialog/__workshop__/activate.tsx +0 -134
- package/src/core/components/dialog/__workshop__/autoFocus.tsx +0 -29
- package/src/core/components/dialog/__workshop__/index.ts +0 -19
- package/src/core/components/dialog/__workshop__/layering.tsx +0 -41
- package/src/core/components/dialog/__workshop__/nested.tsx +0 -72
- package/src/core/components/dialog/__workshop__/onScroll.tsx +0 -39
- package/src/core/components/dialog/__workshop__/panes.tsx +0 -82
- package/src/core/components/dialog/__workshop__/position.tsx +0 -30
- package/src/core/components/dialog/__workshop__/props.tsx +0 -75
- package/src/core/components/dialog/__workshop__/provider.tsx +0 -11
- package/src/core/components/dialog/__workshop__/wrapped.tsx +0 -76
- package/src/core/components/dialog/dialog.tsx +0 -430
- package/src/core/components/dialog/dialogContext.ts +0 -20
- package/src/core/components/dialog/dialogProvider.tsx +0 -35
- package/src/core/components/dialog/index.ts +0 -4
- package/src/core/components/dialog/styles.ts +0 -77
- package/src/core/components/dialog/useDialog.ts +0 -11
- package/src/core/components/hotkeys/__workshop__/index.ts +0 -14
- package/src/core/components/hotkeys/__workshop__/plain.tsx +0 -9
- package/src/core/components/hotkeys/hotkeys.test.tsx +0 -42
- package/src/core/components/hotkeys/hotkeys.tsx +0 -66
- package/src/core/components/hotkeys/index.ts +0 -1
- package/src/core/components/index.ts +0 -9
- package/src/core/components/menu/__workshop__/asComponent.tsx +0 -45
- package/src/core/components/menu/__workshop__/avatarMenu.tsx +0 -51
- package/src/core/components/menu/__workshop__/closableMenuButton.tsx +0 -46
- package/src/core/components/menu/__workshop__/constrainedInBoundary.tsx +0 -134
- package/src/core/components/menu/__workshop__/customMenuItem.tsx +0 -45
- package/src/core/components/menu/__workshop__/customSelectedState.tsx +0 -39
- package/src/core/components/menu/__workshop__/disableFocusOnClose.tsx +0 -40
- package/src/core/components/menu/__workshop__/groups.tsx +0 -156
- package/src/core/components/menu/__workshop__/index.ts +0 -89
- package/src/core/components/menu/__workshop__/menuButton.tsx +0 -80
- package/src/core/components/menu/__workshop__/menuGroupRight.tsx +0 -58
- package/src/core/components/menu/__workshop__/nestedMenu.tsx +0 -22
- package/src/core/components/menu/__workshop__/onCloseMenuButton.tsx +0 -41
- package/src/core/components/menu/__workshop__/selectedItem.tsx +0 -69
- package/src/core/components/menu/__workshop__/shouldFocus.tsx +0 -47
- package/src/core/components/menu/__workshop__/tones.tsx +0 -25
- package/src/core/components/menu/__workshop__/withoutArrow.tsx +0 -32
- package/src/core/components/menu/helpers.ts +0 -82
- package/src/core/components/menu/index.ts +0 -5
- package/src/core/components/menu/menu.spacing.test.tsx +0 -63
- package/src/core/components/menu/menu.test.tsx +0 -125
- package/src/core/components/menu/menu.tsx +0 -178
- package/src/core/components/menu/menuButton.tsx +0 -268
- package/src/core/components/menu/menuContext.ts +0 -18
- package/src/core/components/menu/menuDivider.ts +0 -23
- package/src/core/components/menu/menuGroup.spacing.test.tsx +0 -63
- package/src/core/components/menu/menuGroup.tsx +0 -233
- package/src/core/components/menu/menuItem.spacing.test.tsx +0 -59
- package/src/core/components/menu/menuItem.tsx +0 -197
- package/src/core/components/menu/useMenu.ts +0 -21
- package/src/core/components/menu/useMenuController.ts +0 -231
- package/src/core/components/skeleton/__workshop__/delay.tsx +0 -70
- package/src/core/components/skeleton/__workshop__/index.ts +0 -11
- package/src/core/components/skeleton/__workshop__/skeleton.tsx +0 -64
- package/src/core/components/skeleton/index.ts +0 -2
- package/src/core/components/skeleton/skeleton.tsx +0 -57
- package/src/core/components/skeleton/styles.ts +0 -50
- package/src/core/components/skeleton/textSkeleton.tsx +0 -122
- package/src/core/components/tab/__workshop__/example.tsx +0 -79
- package/src/core/components/tab/__workshop__/index.ts +0 -14
- package/src/core/components/tab/index.ts +0 -3
- package/src/core/components/tab/tab.tsx +0 -101
- package/src/core/components/tab/tabList.test.tsx +0 -48
- package/src/core/components/tab/tabList.tsx +0 -70
- package/src/core/components/tab/tabPanel.tsx +0 -39
- package/src/core/components/toast/__workshop__/hook.tsx +0 -71
- package/src/core/components/toast/__workshop__/index.ts +0 -19
- package/src/core/components/toast/__workshop__/toast.tsx +0 -26
- package/src/core/components/toast/index.ts +0 -4
- package/src/core/components/toast/styles.ts +0 -77
- package/src/core/components/toast/toast.test.tsx +0 -101
- package/src/core/components/toast/toast.tsx +0 -198
- package/src/core/components/toast/toastContext.ts +0 -7
- package/src/core/components/toast/toastLayer.tsx +0 -51
- package/src/core/components/toast/toastProvider.tsx +0 -113
- package/src/core/components/toast/toastState.ts +0 -6
- package/src/core/components/toast/types.ts +0 -26
- package/src/core/components/toast/useToast.ts +0 -25
- package/src/core/components/tree/__workshop__/basic.perf.ts +0 -19
- package/src/core/components/tree/__workshop__/basic.tsx +0 -105
- package/src/core/components/tree/__workshop__/index.ts +0 -21
- package/src/core/components/tree/__workshop__/tabFromElement.tsx +0 -84
- package/src/core/components/tree/helpers.ts +0 -105
- package/src/core/components/tree/index.ts +0 -4
- package/src/core/components/tree/style.ts +0 -105
- package/src/core/components/tree/tree.test.tsx +0 -55
- package/src/core/components/tree/tree.tsx +0 -240
- package/src/core/components/tree/treeContext.ts +0 -10
- package/src/core/components/tree/treeGroup.tsx +0 -28
- package/src/core/components/tree/treeItem.test.tsx +0 -65
- package/src/core/components/tree/treeItem.tsx +0 -224
- package/src/core/components/tree/types.ts +0 -25
- package/src/core/components/tree/useTree.ts +0 -17
- package/src/core/constants.ts +0 -79
- package/src/core/helpers/animation.ts +0 -27
- package/src/core/helpers/element.ts +0 -55
- package/src/core/helpers/focus.ts +0 -100
- package/src/core/helpers/index.ts +0 -4
- package/src/core/helpers/scroll.ts +0 -15
- package/src/core/hooks/index.ts +0 -12
- package/src/core/hooks/useArrayProp.ts +0 -17
- package/src/core/hooks/useClickOutside.test.tsx +0 -482
- package/src/core/hooks/useClickOutside.ts +0 -118
- package/src/core/hooks/useClickOutsideEvent.test.tsx +0 -115
- package/src/core/hooks/useClickOutsideEvent.ts +0 -73
- package/src/core/hooks/useCustomValidity.ts +0 -13
- package/src/core/hooks/useDelayed.test.ts +0 -88
- package/src/core/hooks/useDelayedState.ts +0 -36
- package/src/core/hooks/useElementRect/__workshop__/example.tsx +0 -32
- package/src/core/hooks/useElementRect/__workshop__/index.ts +0 -14
- package/src/core/hooks/useElementRect/index.ts +0 -1
- package/src/core/hooks/useElementRect/useElementRect.ts +0 -13
- package/src/core/hooks/useElementSize.ts +0 -19
- package/src/core/hooks/useForwardedRef.ts +0 -19
- package/src/core/hooks/useGlobalKeyDown.ts +0 -24
- package/src/core/hooks/useIsomorphicEffect.ts +0 -7
- package/src/core/hooks/useMatchMedia.ts +0 -28
- package/src/core/hooks/useMediaIndex/__workshop__/index.ts +0 -14
- package/src/core/hooks/useMediaIndex/__workshop__/test.tsx +0 -14
- package/src/core/hooks/useMediaIndex/index.ts +0 -1
- package/src/core/hooks/useMediaIndex/useMediaIndex.test.tsx +0 -36
- package/src/core/hooks/useMediaIndex/useMediaIndex.ts +0 -102
- package/src/core/hooks/useMounted.ts +0 -15
- package/src/core/hooks/usePrefersDark.hydration.test.tsx +0 -56
- package/src/core/hooks/usePrefersDark.test.tsx +0 -19
- package/src/core/hooks/usePrefersDark.ts +0 -16
- package/src/core/hooks/usePrefersReducedMotion.hydration.test.tsx +0 -56
- package/src/core/hooks/usePrefersReducedMotion.test.tsx +0 -19
- package/src/core/hooks/usePrefersReducedMotion.ts +0 -16
- package/src/core/index.ts +0 -10
- package/src/core/lib/createGlobalScopedContext.ts +0 -36
- package/src/core/lib/globalScope.ts +0 -19
- package/src/core/lib/isRecord.ts +0 -3
- package/src/core/middleware/origin.ts +0 -47
- package/src/core/observers/elementSizeObserver.ts +0 -116
- package/src/core/observers/index.ts +0 -2
- package/src/core/observers/resizeObserver.ts +0 -9
- package/src/core/primitives/_selectable/index.ts +0 -1
- package/src/core/primitives/_selectable/selectable.tsx +0 -15
- package/src/core/primitives/_selectable/style.ts +0 -115
- package/src/core/primitives/avatar/__workshop__/asButton.tsx +0 -14
- package/src/core/primitives/avatar/__workshop__/index.ts +0 -29
- package/src/core/primitives/avatar/__workshop__/stack.tsx +0 -25
- package/src/core/primitives/avatar/__workshop__/withinButton.tsx +0 -63
- package/src/core/primitives/avatar/__workshop__/withinMenuItem.tsx +0 -70
- package/src/core/primitives/avatar/avatar.tsx +0 -200
- package/src/core/primitives/avatar/avatarCounter.tsx +0 -91
- package/src/core/primitives/avatar/avatarStack.tsx +0 -100
- package/src/core/primitives/avatar/index.ts +0 -4
- package/src/core/primitives/avatar/styles.ts +0 -167
- package/src/core/primitives/avatar/types.ts +0 -17
- package/src/core/primitives/badge/__workshop__/index.ts +0 -19
- package/src/core/primitives/badge/__workshop__/props.tsx +0 -30
- package/src/core/primitives/badge/__workshop__/tones.tsx +0 -21
- package/src/core/primitives/badge/badge.test.tsx +0 -16
- package/src/core/primitives/badge/badge.tsx +0 -70
- package/src/core/primitives/badge/index.ts +0 -1
- package/src/core/primitives/badge/styles.ts +0 -20
- package/src/core/primitives/badge/types.ts +0 -8
- package/src/core/primitives/box/__workshop__/asComponent.tsx +0 -31
- package/src/core/primitives/box/__workshop__/index.ts +0 -24
- package/src/core/primitives/box/__workshop__/props.tsx +0 -20
- package/src/core/primitives/box/__workshop__/responsive.tsx +0 -20
- package/src/core/primitives/box/box.test.tsx +0 -142
- package/src/core/primitives/box/box.tsx +0 -154
- package/src/core/primitives/box/index.ts +0 -1
- package/src/core/primitives/button/__workshop__/custom.tsx +0 -51
- package/src/core/primitives/button/__workshop__/customIcons.tsx +0 -30
- package/src/core/primitives/button/__workshop__/disabled.tsx +0 -74
- package/src/core/primitives/button/__workshop__/index.ts +0 -59
- package/src/core/primitives/button/__workshop__/labelUploadButton.tsx +0 -36
- package/src/core/primitives/button/__workshop__/mixedChildren.tsx +0 -12
- package/src/core/primitives/button/__workshop__/props.tsx +0 -55
- package/src/core/primitives/button/__workshop__/stacked.tsx +0 -58
- package/src/core/primitives/button/__workshop__/styled1.tsx +0 -17
- package/src/core/primitives/button/__workshop__/styled2.tsx +0 -19
- package/src/core/primitives/button/__workshop__/uploadButton.tsx +0 -28
- package/src/core/primitives/button/button.test.tsx +0 -82
- package/src/core/primitives/button/button.tsx +0 -210
- package/src/core/primitives/button/index.ts +0 -1
- package/src/core/primitives/button/styles.ts +0 -117
- package/src/core/primitives/card/__workshop__/allTones.tsx +0 -25
- package/src/core/primitives/card/__workshop__/asButton.tsx +0 -107
- package/src/core/primitives/card/__workshop__/asComponent.tsx +0 -27
- package/src/core/primitives/card/__workshop__/checkered.tsx +0 -26
- package/src/core/primitives/card/__workshop__/index.ts +0 -18
- package/src/core/primitives/card/__workshop__/interactive.tsx +0 -30
- package/src/core/primitives/card/__workshop__/listNavigation.tsx +0 -80
- package/src/core/primitives/card/__workshop__/props.tsx +0 -53
- package/src/core/primitives/card/__workshop__/selected.tsx +0 -94
- package/src/core/primitives/card/__workshop__/styled.tsx +0 -14
- package/src/core/primitives/card/card.tsx +0 -128
- package/src/core/primitives/card/index.ts +0 -2
- package/src/core/primitives/card/styles.ts +0 -147
- package/src/core/primitives/card/types.ts +0 -11
- package/src/core/primitives/checkbox/__workshop__/example.tsx +0 -25
- package/src/core/primitives/checkbox/__workshop__/index.ts +0 -13
- package/src/core/primitives/checkbox/__workshop__/props.tsx +0 -33
- package/src/core/primitives/checkbox/__workshop__/readOnly.tsx +0 -16
- package/src/core/primitives/checkbox/__workshop__/tones.tsx +0 -28
- package/src/core/primitives/checkbox/checkbox.tsx +0 -73
- package/src/core/primitives/checkbox/index.ts +0 -1
- package/src/core/primitives/checkbox/styles.ts +0 -128
- package/src/core/primitives/code/__workshop__/index.ts +0 -19
- package/src/core/primitives/code/__workshop__/opticalAlignment.tsx +0 -48
- package/src/core/primitives/code/__workshop__/props.tsx +0 -21
- package/src/core/primitives/code/code.tsx +0 -55
- package/src/core/primitives/code/index.ts +0 -1
- package/src/core/primitives/code/refractor.tsx +0 -20
- package/src/core/primitives/code/styles.ts +0 -77
- package/src/core/primitives/container/__workshop__/example.tsx +0 -20
- package/src/core/primitives/container/__workshop__/index.ts +0 -8
- package/src/core/primitives/container/container.tsx +0 -54
- package/src/core/primitives/container/index.ts +0 -2
- package/src/core/primitives/container/styles.ts +0 -23
- package/src/core/primitives/container/types.ts +0 -6
- package/src/core/primitives/flex/__workshop__/example.tsx +0 -34
- package/src/core/primitives/flex/__workshop__/gap.tsx +0 -25
- package/src/core/primitives/flex/__workshop__/index.ts +0 -11
- package/src/core/primitives/flex/flex.tsx +0 -64
- package/src/core/primitives/flex/index.ts +0 -1
- package/src/core/primitives/grid/__workshop__/index.ts +0 -10
- package/src/core/primitives/grid/__workshop__/responsive.tsx +0 -54
- package/src/core/primitives/grid/grid.test.tsx +0 -80
- package/src/core/primitives/grid/grid.tsx +0 -74
- package/src/core/primitives/grid/index.ts +0 -1
- package/src/core/primitives/heading/__workshop__/index.ts +0 -19
- package/src/core/primitives/heading/__workshop__/opticalAlignment.tsx +0 -54
- package/src/core/primitives/heading/__workshop__/plain.tsx +0 -32
- package/src/core/primitives/heading/heading.tsx +0 -88
- package/src/core/primitives/heading/index.ts +0 -1
- package/src/core/primitives/heading/styles.ts +0 -64
- package/src/core/primitives/heading/types.ts +0 -7
- package/src/core/primitives/index.ts +0 -27
- package/src/core/primitives/inline/__workshop__/index.ts +0 -8
- package/src/core/primitives/inline/__workshop__/plain.tsx +0 -27
- package/src/core/primitives/inline/index.ts +0 -1
- package/src/core/primitives/inline/inline.test.tsx +0 -51
- package/src/core/primitives/inline/inline.tsx +0 -63
- package/src/core/primitives/inline/styles.ts +0 -32
- package/src/core/primitives/inline/types.ts +0 -6
- package/src/core/primitives/kbd/__workshop__/index.ts +0 -8
- package/src/core/primitives/kbd/__workshop__/plain.tsx +0 -9
- package/src/core/primitives/kbd/index.ts +0 -1
- package/src/core/primitives/kbd/kbd.tsx +0 -69
- package/src/core/primitives/label/__workshop__/index.ts +0 -19
- package/src/core/primitives/label/__workshop__/opticalAlignment.tsx +0 -48
- package/src/core/primitives/label/__workshop__/plain.tsx +0 -24
- package/src/core/primitives/label/index.ts +0 -1
- package/src/core/primitives/label/label.tsx +0 -87
- package/src/core/primitives/label/styles.ts +0 -45
- package/src/core/primitives/popover/__workshop__/AlignedStory.tsx +0 -77
- package/src/core/primitives/popover/__workshop__/MarginsStory.tsx +0 -31
- package/src/core/primitives/popover/__workshop__/MatchReferenceWidthStory.tsx +0 -32
- package/src/core/primitives/popover/__workshop__/OpenOnMountStory.tsx +0 -11
- package/src/core/primitives/popover/__workshop__/PlainStory.tsx +0 -71
- package/src/core/primitives/popover/__workshop__/RecursiveStory.tsx +0 -71
- package/src/core/primitives/popover/__workshop__/SidePanelStory.tsx +0 -89
- package/src/core/primitives/popover/__workshop__/TestStory.tsx +0 -97
- package/src/core/primitives/popover/__workshop__/index.ts +0 -49
- package/src/core/primitives/popover/constants.ts +0 -22
- package/src/core/primitives/popover/floating-ui/size.ts +0 -99
- package/src/core/primitives/popover/helpers.ts +0 -31
- package/src/core/primitives/popover/index.ts +0 -2
- package/src/core/primitives/popover/popover.tsx +0 -511
- package/src/core/primitives/popover/popoverCard.tsx +0 -166
- package/src/core/primitives/popover/types.ts +0 -7
- package/src/core/primitives/radio/__workshop__/example.tsx +0 -63
- package/src/core/primitives/radio/__workshop__/index.ts +0 -11
- package/src/core/primitives/radio/__workshop__/plain.tsx +0 -28
- package/src/core/primitives/radio/index.ts +0 -1
- package/src/core/primitives/radio/radio.tsx +0 -51
- package/src/core/primitives/radio/styles.ts +0 -117
- package/src/core/primitives/select/__workshop__/index.ts +0 -11
- package/src/core/primitives/select/__workshop__/plain.tsx +0 -27
- package/src/core/primitives/select/__workshop__/readOnly.tsx +0 -23
- package/src/core/primitives/select/index.ts +0 -1
- package/src/core/primitives/select/select.test.tsx +0 -52
- package/src/core/primitives/select/select.tsx +0 -94
- package/src/core/primitives/select/styles.ts +0 -166
- package/src/core/primitives/spinner/__workshop__/Props.tsx +0 -15
- package/src/core/primitives/spinner/__workshop__/index.ts +0 -14
- package/src/core/primitives/spinner/index.ts +0 -1
- package/src/core/primitives/spinner/spinner.tsx +0 -46
- package/src/core/primitives/stack/__workshop__/index.ts +0 -14
- package/src/core/primitives/stack/__workshop__/plain.tsx +0 -32
- package/src/core/primitives/stack/index.ts +0 -1
- package/src/core/primitives/stack/stack.test.tsx +0 -57
- package/src/core/primitives/stack/stack.tsx +0 -57
- package/src/core/primitives/stack/styles.ts +0 -32
- package/src/core/primitives/switch/__workshop__/example.tsx +0 -23
- package/src/core/primitives/switch/__workshop__/index.ts +0 -11
- package/src/core/primitives/switch/__workshop__/props.tsx +0 -21
- package/src/core/primitives/switch/index.ts +0 -1
- package/src/core/primitives/switch/styles.ts +0 -173
- package/src/core/primitives/switch/switch.tsx +0 -68
- package/src/core/primitives/text/__workshop__/colored.tsx +0 -30
- package/src/core/primitives/text/__workshop__/example.tsx +0 -39
- package/src/core/primitives/text/__workshop__/index.ts +0 -24
- package/src/core/primitives/text/__workshop__/opticalAlignment.tsx +0 -48
- package/src/core/primitives/text/index.ts +0 -1
- package/src/core/primitives/text/styles.ts +0 -74
- package/src/core/primitives/text/text.tsx +0 -89
- package/src/core/primitives/textArea/__workshop__/index.ts +0 -14
- package/src/core/primitives/textArea/__workshop__/plain.tsx +0 -50
- package/src/core/primitives/textArea/index.ts +0 -1
- package/src/core/primitives/textArea/textArea.tsx +0 -120
- package/src/core/primitives/textInput/__workshop__/clearButton.tsx +0 -30
- package/src/core/primitives/textInput/__workshop__/customValidity.tsx +0 -17
- package/src/core/primitives/textInput/__workshop__/index.ts +0 -44
- package/src/core/primitives/textInput/__workshop__/plain.tsx +0 -98
- package/src/core/primitives/textInput/__workshop__/readOnly.tsx +0 -9
- package/src/core/primitives/textInput/__workshop__/states.tsx +0 -53
- package/src/core/primitives/textInput/__workshop__/tones.tsx +0 -277
- package/src/core/primitives/textInput/__workshop__/typed.tsx +0 -23
- package/src/core/primitives/textInput/index.ts +0 -1
- package/src/core/primitives/textInput/textInput.test.tsx +0 -48
- package/src/core/primitives/textInput/textInput.tsx +0 -395
- package/src/core/primitives/tooltip/__workshop__/customPortal.tsx +0 -102
- package/src/core/primitives/tooltip/__workshop__/index.ts +0 -29
- package/src/core/primitives/tooltip/__workshop__/overflowingBoundary.tsx +0 -74
- package/src/core/primitives/tooltip/__workshop__/props.tsx +0 -107
- package/src/core/primitives/tooltip/__workshop__/resizableBoundary.tsx +0 -86
- package/src/core/primitives/tooltip/constants.ts +0 -23
- package/src/core/primitives/tooltip/index.ts +0 -2
- package/src/core/primitives/tooltip/tooltip.test.tsx +0 -452
- package/src/core/primitives/tooltip/tooltip.tsx +0 -495
- package/src/core/primitives/tooltip/tooltipCard.tsx +0 -109
- package/src/core/primitives/tooltip/tooltipDelayGroup/index.ts +0 -4
- package/src/core/primitives/tooltip/tooltipDelayGroup/tooltipDelayGroupContext.tsx +0 -11
- package/src/core/primitives/tooltip/tooltipDelayGroup/tooltipDelayGroupProvider.tsx +0 -57
- package/src/core/primitives/tooltip/tooltipDelayGroup/types.ts +0 -10
- package/src/core/primitives/tooltip/tooltipDelayGroup/useTooltipDelayGroup.ts +0 -13
- package/src/core/primitives/types.ts +0 -179
- package/src/core/styles/border/borderStyle.ts +0 -56
- package/src/core/styles/border/index.ts +0 -2
- package/src/core/styles/border/types.ts +0 -10
- package/src/core/styles/box/boxStyle.ts +0 -69
- package/src/core/styles/box/index.ts +0 -2
- package/src/core/styles/box/types.ts +0 -11
- package/src/core/styles/card/_cardColorStyle.ts +0 -108
- package/src/core/styles/card/index.ts +0 -1
- package/src/core/styles/flex/flexItemStyle.ts +0 -27
- package/src/core/styles/flex/flexStyle.ts +0 -70
- package/src/core/styles/flex/index.ts +0 -3
- package/src/core/styles/flex/types.ts +0 -25
- package/src/core/styles/focusRing/index.ts +0 -25
- package/src/core/styles/font/codeFontStyle.ts +0 -13
- package/src/core/styles/font/headingFontStyle.ts +0 -13
- package/src/core/styles/font/index.ts +0 -6
- package/src/core/styles/font/labelFontStyle.ts +0 -13
- package/src/core/styles/font/responsiveFont.ts +0 -103
- package/src/core/styles/font/textAlignStyle.ts +0 -19
- package/src/core/styles/font/textFontStyle.ts +0 -13
- package/src/core/styles/font/types.ts +0 -35
- package/src/core/styles/grid/gridItemStyle.ts +0 -82
- package/src/core/styles/grid/gridStyle.ts +0 -108
- package/src/core/styles/grid/index.ts +0 -3
- package/src/core/styles/grid/types.ts +0 -37
- package/src/core/styles/helpers.ts +0 -72
- package/src/core/styles/index.ts +0 -5
- package/src/core/styles/input/index.ts +0 -2
- package/src/core/styles/input/responsiveInputPaddingStyle.ts +0 -78
- package/src/core/styles/input/textInputStyle.ts +0 -263
- package/src/core/styles/internal.ts +0 -12
- package/src/core/styles/margin/index.ts +0 -2
- package/src/core/styles/margin/marginStyle.ts +0 -21
- package/src/core/styles/margin/marginsStyle.test.ts +0 -33
- package/src/core/styles/margin/types.ts +0 -9
- package/src/core/styles/padding/index.ts +0 -2
- package/src/core/styles/padding/paddingStyle.test.ts +0 -33
- package/src/core/styles/padding/paddingStyle.ts +0 -21
- package/src/core/styles/padding/types.ts +0 -12
- package/src/core/styles/radius/index.ts +0 -2
- package/src/core/styles/radius/radiusStyle.ts +0 -23
- package/src/core/styles/radius/types.ts +0 -8
- package/src/core/styles/shadow/index.ts +0 -2
- package/src/core/styles/shadow/shadowStyle.ts +0 -29
- package/src/core/styles/shadow/types.ts +0 -6
- package/src/core/styles/types.ts +0 -8
- package/src/core/theme/__workshop__/build/Root.tsx +0 -367
- package/src/core/theme/__workshop__/build/helpers.ts +0 -46
- package/src/core/theme/__workshop__/build/story.tsx +0 -465
- package/src/core/theme/__workshop__/canvas.tsx +0 -352
- package/src/core/theme/__workshop__/color.tsx +0 -62
- package/src/core/theme/__workshop__/debug/story.tsx +0 -408
- package/src/core/theme/__workshop__/index.ts +0 -39
- package/src/core/theme/__workshop__/layer.tsx +0 -78
- package/src/core/theme/__workshop__/nestedProvider.tsx +0 -15
- package/src/core/theme/index.ts +0 -5
- package/src/core/theme/theme.test.tsx +0 -73
- package/src/core/theme/themeColorProvider.tsx +0 -29
- package/src/core/theme/themeContext.ts +0 -10
- package/src/core/theme/themeProvider.tsx +0 -62
- package/src/core/theme/types.ts +0 -12
- package/src/core/theme/useRootTheme.ts +0 -17
- package/src/core/theme/useTheme.ts +0 -16
- package/src/core/types/avatar.ts +0 -14
- package/src/core/types/badge.ts +0 -12
- package/src/core/types/box.ts +0 -19
- package/src/core/types/button.ts +0 -21
- package/src/core/types/card.ts +0 -6
- package/src/core/types/component.ts +0 -32
- package/src/core/types/dialog.ts +0 -4
- package/src/core/types/flex.ts +0 -30
- package/src/core/types/grid.ts +0 -14
- package/src/core/types/gridItem.ts +0 -29
- package/src/core/types/index.ts +0 -15
- package/src/core/types/placement.ts +0 -18
- package/src/core/types/popover.ts +0 -4
- package/src/core/types/radius.ts +0 -4
- package/src/core/types/selectable.ts +0 -6
- package/src/core/types/text.ts +0 -4
- package/src/core/utils/arrow/arrow.tsx +0 -123
- package/src/core/utils/arrow/cmds.ts +0 -91
- package/src/core/utils/arrow/index.ts +0 -1
- package/src/core/utils/boundaryElement/__workshop__/index.ts +0 -14
- package/src/core/utils/boundaryElement/__workshop__/plain.tsx +0 -19
- package/src/core/utils/boundaryElement/boundaryElement.test.tsx +0 -100
- package/src/core/utils/boundaryElement/boundaryElementContext.ts +0 -7
- package/src/core/utils/boundaryElement/boundaryElementProvider.tsx +0 -24
- package/src/core/utils/boundaryElement/index.ts +0 -3
- package/src/core/utils/boundaryElement/types.ts +0 -7
- package/src/core/utils/boundaryElement/useBoundaryElement.ts +0 -26
- package/src/core/utils/conditionalWrapper/conditionalWrapper.tsx +0 -21
- package/src/core/utils/conditionalWrapper/index.ts +0 -1
- package/src/core/utils/elementQuery/__workshop__/customMedia.tsx +0 -30
- package/src/core/utils/elementQuery/__workshop__/index.ts +0 -14
- package/src/core/utils/elementQuery/elementQuery.tsx +0 -53
- package/src/core/utils/elementQuery/helpers.ts +0 -27
- package/src/core/utils/elementQuery/index.ts +0 -1
- package/src/core/utils/errorBoundary.tsx +0 -48
- package/src/core/utils/getElementRef.ts +0 -26
- package/src/core/utils/index.ts +0 -9
- package/src/core/utils/layer/__workshop__/_debug.tsx +0 -17
- package/src/core/utils/layer/__workshop__/index.ts +0 -24
- package/src/core/utils/layer/__workshop__/multipleRoots.tsx +0 -49
- package/src/core/utils/layer/__workshop__/nested.tsx +0 -98
- package/src/core/utils/layer/__workshop__/responsiveZOffset.tsx +0 -13
- package/src/core/utils/layer/getLayerContext.test.ts +0 -30
- package/src/core/utils/layer/getLayerContext.ts +0 -21
- package/src/core/utils/layer/index.ts +0 -4
- package/src/core/utils/layer/layer.test.tsx +0 -108
- package/src/core/utils/layer/layer.tsx +0 -98
- package/src/core/utils/layer/layerContext.ts +0 -7
- package/src/core/utils/layer/layerProvider.tsx +0 -111
- package/src/core/utils/layer/types.ts +0 -9
- package/src/core/utils/layer/useLayer.ts +0 -26
- package/src/core/utils/portal/__workshop__/index.ts +0 -14
- package/src/core/utils/portal/__workshop__/named.tsx +0 -63
- package/src/core/utils/portal/index.ts +0 -4
- package/src/core/utils/portal/portal.test.tsx +0 -103
- package/src/core/utils/portal/portal.ts +0 -32
- package/src/core/utils/portal/portalContext.ts +0 -31
- package/src/core/utils/portal/portalProvider.tsx +0 -47
- package/src/core/utils/portal/types.ts +0 -9
- package/src/core/utils/portal/usePortal.ts +0 -25
- package/src/core/utils/spanWithTextOverflow.tsx +0 -10
- package/src/core/utils/srOnly/index.ts +0 -1
- package/src/core/utils/srOnly/srOnly.tsx +0 -36
- package/src/core/utils/virtualList/__workshop__/changingProps.tsx +0 -36
- package/src/core/utils/virtualList/__workshop__/elementScroll.tsx +0 -24
- package/src/core/utils/virtualList/__workshop__/index.ts +0 -29
- package/src/core/utils/virtualList/__workshop__/infiniteList.tsx +0 -48
- package/src/core/utils/virtualList/__workshop__/windowScrolll.tsx +0 -22
- package/src/core/utils/virtualList/index.ts +0 -1
- package/src/core/utils/virtualList/virtualList.tsx +0 -200
- package/src/theme/build/_deprecated/color/_selectable/createSelectableTones.ts +0 -76
- package/src/theme/build/_deprecated/color/_solid/createSolidTones.ts +0 -54
- package/src/theme/build/_deprecated/color/button/createButtonModes.ts +0 -22
- package/src/theme/build/_deprecated/color/button/createButtonTones.ts +0 -55
- package/src/theme/build/_deprecated/color/card/createCardStates.ts +0 -60
- package/src/theme/build/_deprecated/color/color.test.ts +0 -63
- package/src/theme/build/_deprecated/color/defaults.ts +0 -390
- package/src/theme/build/_deprecated/color/factory.ts +0 -142
- package/src/theme/build/_deprecated/color/index.ts +0 -1
- package/src/theme/build/_deprecated/color/input/createInputModes.ts +0 -81
- package/src/theme/build/_deprecated/color/muted/createMuted.ts +0 -54
- package/src/theme/build/_deprecated/color/spot/createSpot.ts +0 -20
- package/src/theme/build/buildColorTheme.test.ts +0 -183
- package/src/theme/build/buildColorTheme.ts +0 -499
- package/src/theme/build/buildTheme.test.ts +0 -87
- package/src/theme/build/buildTheme.ts +0 -36
- package/src/theme/build/colorToken/colorToken.ts +0 -19
- package/src/theme/build/colorToken/compileColorToken.ts +0 -21
- package/src/theme/build/colorToken/index.ts +0 -2
- package/src/theme/build/colorToken/types.ts +0 -6
- package/src/theme/build/index.ts +0 -3
- package/src/theme/build/lib/color-fns/blend/index.ts +0 -3
- package/src/theme/build/lib/color-fns/blend/mix.ts +0 -21
- package/src/theme/build/lib/color-fns/blend/multiply.ts +0 -18
- package/src/theme/build/lib/color-fns/blend/screen.ts +0 -18
- package/src/theme/build/lib/color-fns/color-fns.test.ts +0 -68
- package/src/theme/build/lib/color-fns/contrastRatio.ts +0 -31
- package/src/theme/build/lib/color-fns/convert.ts +0 -150
- package/src/theme/build/lib/color-fns/index.ts +0 -6
- package/src/theme/build/lib/color-fns/parse.ts +0 -60
- package/src/theme/build/lib/color-fns/rgba.ts +0 -10
- package/src/theme/build/lib/color-fns/types.ts +0 -28
- package/src/theme/build/lib/isRecord.ts +0 -3
- package/src/theme/build/lib/lazy.ts +0 -30
- package/src/theme/build/lib/utils.ts +0 -19
- package/src/theme/build/merge.ts +0 -26
- package/src/theme/build/mixThemeColor.test.ts +0 -21
- package/src/theme/build/mixThemeColor.ts +0 -53
- package/src/theme/build/renderColorTheme.ts +0 -532
- package/src/theme/build/renderColorValue.ts +0 -85
- package/src/theme/build/resolveColorTokens.ts +0 -268
- package/src/theme/build/theme.test.ts +0 -27
- package/src/theme/config/helpers.ts +0 -55
- package/src/theme/config/index.ts +0 -4
- package/src/theme/config/system.ts +0 -115
- package/src/theme/config/tokens/color/index.ts +0 -1
- package/src/theme/config/tokens/color/types.ts +0 -133
- package/src/theme/config/tokens/index.ts +0 -4
- package/src/theme/config/tokens/parseTokenKey.test.ts +0 -64
- package/src/theme/config/tokens/parseTokenKey.ts +0 -67
- package/src/theme/config/tokens/parseTokenValue.test.ts +0 -42
- package/src/theme/config/tokens/parseTokenValue.ts +0 -133
- package/src/theme/config/tokens/types.ts +0 -53
- package/src/theme/config/types.ts +0 -46
- package/src/theme/defaults/colorPalette.ts +0 -5
- package/src/theme/defaults/colorTokens.ts +0 -648
- package/src/theme/defaults/config.ts +0 -84
- package/src/theme/defaults/fonts.ts +0 -227
- package/src/theme/getScopedTheme.ts +0 -98
- package/src/theme/index.ts +0 -5
- package/src/theme/system/avatar.ts +0 -14
- package/src/theme/system/color/_constants.ts +0 -45
- package/src/theme/system/color/_helpers.ts +0 -24
- package/src/theme/system/color/_system.ts +0 -40
- package/src/theme/system/color/avatar.ts +0 -15
- package/src/theme/system/color/badge.ts +0 -16
- package/src/theme/system/color/button.ts +0 -17
- package/src/theme/system/color/color.ts +0 -34
- package/src/theme/system/color/index.ts +0 -13
- package/src/theme/system/color/input.ts +0 -25
- package/src/theme/system/color/kbd.ts +0 -8
- package/src/theme/system/color/selectable.ts +0 -12
- package/src/theme/system/color/shadow.ts +0 -7
- package/src/theme/system/color/state.ts +0 -43
- package/src/theme/system/color/syntax.ts +0 -41
- package/src/theme/system/css.ts +0 -14
- package/src/theme/system/focusRing.ts +0 -7
- package/src/theme/system/font.ts +0 -52
- package/src/theme/system/index.ts +0 -11
- package/src/theme/system/input.ts +0 -33
- package/src/theme/system/layer.ts +0 -8
- package/src/theme/system/shadow.ts +0 -19
- package/src/theme/system/styles.ts +0 -13
- package/src/theme/system/theme.ts +0 -138
- package/src/theme/system/v0/avatar.ts +0 -10
- package/src/theme/system/v0/color/_generic.ts +0 -35
- package/src/theme/system/v0/color/_system.ts +0 -17
- package/src/theme/system/v0/color/base.ts +0 -21
- package/src/theme/system/v0/color/button.ts +0 -42
- package/src/theme/system/v0/color/card.ts +0 -19
- package/src/theme/system/v0/color/color.ts +0 -40
- package/src/theme/system/v0/color/index.ts +0 -11
- package/src/theme/system/v0/color/input.ts +0 -34
- package/src/theme/system/v0/color/muted.ts +0 -26
- package/src/theme/system/v0/color/selectable.ts +0 -31
- package/src/theme/system/v0/color/solid.ts +0 -26
- package/src/theme/system/v0/color/spot.ts +0 -13
- package/src/theme/system/v0/index.ts +0 -3
- package/src/theme/system/v0/input.ts +0 -23
- package/src/theme/versioning/getTheme_v2.ts +0 -57
- package/src/theme/versioning/index.ts +0 -5
- package/src/theme/versioning/is_v0.ts +0 -6
- package/src/theme/versioning/is_v2.ts +0 -6
- package/src/theme/versioning/themeColor_v0_v2.ts +0 -243
- package/src/theme/versioning/themeColor_v2_v2_9.ts +0 -49
- package/src/theme/versioning/v0_v2.ts +0 -94
- package/src/theme/versioning/v2_v0.ts +0 -135
|
@@ -0,0 +1,3401 @@
|
|
|
1
|
+
//#region src/library/entries/heroes.entries.ts
|
|
2
|
+
var pic = (seed, alt, w = 1600, h = 1e3) => ({
|
|
3
|
+
url: `https://picsum.photos/seed/${seed}/${w}/${h}`,
|
|
4
|
+
alt,
|
|
5
|
+
name: `${seed}.jpg`,
|
|
6
|
+
width: w,
|
|
7
|
+
height: h
|
|
8
|
+
});
|
|
9
|
+
var btn = (label, href, style = "primary", extra = {}) => ({
|
|
10
|
+
label,
|
|
11
|
+
href,
|
|
12
|
+
type: href.startsWith("http") ? "url" : href.startsWith("#") ? "section" : "page",
|
|
13
|
+
style,
|
|
14
|
+
...extra
|
|
15
|
+
});
|
|
16
|
+
var eyebrow = {
|
|
17
|
+
name: "eyebrow",
|
|
18
|
+
title: "Eyebrow",
|
|
19
|
+
type: "string",
|
|
20
|
+
validation: { max: 40 }
|
|
21
|
+
};
|
|
22
|
+
var heading = (rec = 60) => ({
|
|
23
|
+
name: "heading",
|
|
24
|
+
title: "Heading",
|
|
25
|
+
type: "string",
|
|
26
|
+
validation: {
|
|
27
|
+
required: true,
|
|
28
|
+
max: 100,
|
|
29
|
+
recommendedMax: rec
|
|
30
|
+
},
|
|
31
|
+
ai: true,
|
|
32
|
+
localized: true
|
|
33
|
+
});
|
|
34
|
+
var body = (rec = 180) => ({
|
|
35
|
+
name: "body",
|
|
36
|
+
title: "Body",
|
|
37
|
+
type: "text",
|
|
38
|
+
rows: 3,
|
|
39
|
+
validation: { recommendedMax: rec },
|
|
40
|
+
ai: true,
|
|
41
|
+
localized: true
|
|
42
|
+
});
|
|
43
|
+
var primary = {
|
|
44
|
+
name: "primaryButton",
|
|
45
|
+
title: "Primary button",
|
|
46
|
+
type: "button"
|
|
47
|
+
};
|
|
48
|
+
var secondary = {
|
|
49
|
+
name: "secondaryButton",
|
|
50
|
+
title: "Secondary button",
|
|
51
|
+
type: "button"
|
|
52
|
+
};
|
|
53
|
+
var GROUPS = [
|
|
54
|
+
{
|
|
55
|
+
name: "content",
|
|
56
|
+
title: "Content",
|
|
57
|
+
default: true
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "media",
|
|
61
|
+
title: "Media"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "design",
|
|
65
|
+
title: "Design"
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
var theme = {
|
|
69
|
+
name: "theme",
|
|
70
|
+
title: "Theme",
|
|
71
|
+
type: "select",
|
|
72
|
+
group: "design",
|
|
73
|
+
layout: "segmented",
|
|
74
|
+
initialValue: "light",
|
|
75
|
+
options: [
|
|
76
|
+
{
|
|
77
|
+
value: "light",
|
|
78
|
+
label: "Light"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
value: "dark",
|
|
82
|
+
label: "Dark"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
value: "brand",
|
|
86
|
+
label: "Brand"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
var overlay = {
|
|
91
|
+
name: "overlay",
|
|
92
|
+
title: "Image overlay",
|
|
93
|
+
type: "number",
|
|
94
|
+
group: "design",
|
|
95
|
+
layout: "slider",
|
|
96
|
+
numeric: {
|
|
97
|
+
min: 0,
|
|
98
|
+
max: 80,
|
|
99
|
+
step: 5,
|
|
100
|
+
unit: "%"
|
|
101
|
+
},
|
|
102
|
+
initialValue: 35
|
|
103
|
+
};
|
|
104
|
+
var spacing = {
|
|
105
|
+
name: "spacing",
|
|
106
|
+
title: "Spacing",
|
|
107
|
+
type: "select",
|
|
108
|
+
group: "design",
|
|
109
|
+
layout: "segmented",
|
|
110
|
+
initialValue: "default",
|
|
111
|
+
options: [
|
|
112
|
+
{
|
|
113
|
+
value: "compact",
|
|
114
|
+
label: "Compact"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
value: "default",
|
|
118
|
+
label: "Default"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
value: "spacious",
|
|
122
|
+
label: "Spacious"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
};
|
|
126
|
+
var align = {
|
|
127
|
+
name: "alignment",
|
|
128
|
+
title: "Alignment",
|
|
129
|
+
type: "select",
|
|
130
|
+
group: "design",
|
|
131
|
+
layout: "segmented",
|
|
132
|
+
initialValue: "left",
|
|
133
|
+
options: [{
|
|
134
|
+
value: "left",
|
|
135
|
+
label: "Left"
|
|
136
|
+
}, {
|
|
137
|
+
value: "center",
|
|
138
|
+
label: "Centered"
|
|
139
|
+
}]
|
|
140
|
+
};
|
|
141
|
+
var ENTRIES = [
|
|
142
|
+
{
|
|
143
|
+
id: "hero-split-media",
|
|
144
|
+
previewLayout: "split",
|
|
145
|
+
label: "Split hero with media switch",
|
|
146
|
+
proves: "One media slot that can be an image, a video or a small gallery · a phone crop · benefit list as plain strings",
|
|
147
|
+
category: "Heroes",
|
|
148
|
+
tags: [
|
|
149
|
+
"variants",
|
|
150
|
+
"conditional",
|
|
151
|
+
"media-heavy",
|
|
152
|
+
"a11y",
|
|
153
|
+
"repeater"
|
|
154
|
+
],
|
|
155
|
+
brand: "Caldera",
|
|
156
|
+
schema: {
|
|
157
|
+
name: "mediaHero",
|
|
158
|
+
title: "Media hero",
|
|
159
|
+
kind: "block",
|
|
160
|
+
icon: "layout-columns",
|
|
161
|
+
category: "Heroes",
|
|
162
|
+
description: "Copy on one side, an image, video or gallery on the other.",
|
|
163
|
+
variants: [
|
|
164
|
+
{
|
|
165
|
+
id: "media-right",
|
|
166
|
+
name: "Media right"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: "media-left",
|
|
170
|
+
name: "Media left"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "overlap",
|
|
174
|
+
name: "Overlapping card",
|
|
175
|
+
description: "Copy sits on a card over the media edge"
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
preview: {
|
|
179
|
+
title: "heading",
|
|
180
|
+
subtitle: "eyebrow",
|
|
181
|
+
media: "image"
|
|
182
|
+
},
|
|
183
|
+
groups: GROUPS,
|
|
184
|
+
fields: [
|
|
185
|
+
eyebrow,
|
|
186
|
+
heading(),
|
|
187
|
+
body(),
|
|
188
|
+
{
|
|
189
|
+
name: "points",
|
|
190
|
+
title: "Key points",
|
|
191
|
+
type: "array",
|
|
192
|
+
itemNoun: "point",
|
|
193
|
+
validation: { max: 3 },
|
|
194
|
+
of: [{
|
|
195
|
+
name: "point",
|
|
196
|
+
title: "Point",
|
|
197
|
+
type: "string",
|
|
198
|
+
validation: { max: 48 }
|
|
199
|
+
}]
|
|
200
|
+
},
|
|
201
|
+
primary,
|
|
202
|
+
secondary,
|
|
203
|
+
{
|
|
204
|
+
name: "mediaType",
|
|
205
|
+
title: "Media",
|
|
206
|
+
type: "select",
|
|
207
|
+
group: "media",
|
|
208
|
+
layout: "segmented",
|
|
209
|
+
initialValue: "image",
|
|
210
|
+
options: [
|
|
211
|
+
{
|
|
212
|
+
value: "image",
|
|
213
|
+
label: "Image"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
value: "video",
|
|
217
|
+
label: "Video"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
value: "gallery",
|
|
221
|
+
label: "Gallery"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "image",
|
|
227
|
+
title: "Image",
|
|
228
|
+
type: "image",
|
|
229
|
+
group: "media",
|
|
230
|
+
hidden: {
|
|
231
|
+
field: "mediaType",
|
|
232
|
+
equals: "image"
|
|
233
|
+
},
|
|
234
|
+
validation: { required: true },
|
|
235
|
+
description: "At least 1600px wide."
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "video",
|
|
239
|
+
title: "Video",
|
|
240
|
+
type: "video",
|
|
241
|
+
group: "media",
|
|
242
|
+
hidden: {
|
|
243
|
+
field: "mediaType",
|
|
244
|
+
equals: "video"
|
|
245
|
+
},
|
|
246
|
+
validation: { required: true },
|
|
247
|
+
description: "Plays muted and loops."
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "poster",
|
|
251
|
+
title: "Poster",
|
|
252
|
+
type: "image",
|
|
253
|
+
group: "media",
|
|
254
|
+
hidden: {
|
|
255
|
+
field: "mediaType",
|
|
256
|
+
equals: "video"
|
|
257
|
+
},
|
|
258
|
+
validation: { required: true }
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: "gallery",
|
|
262
|
+
title: "Gallery",
|
|
263
|
+
type: "array",
|
|
264
|
+
group: "media",
|
|
265
|
+
arrayLayout: "grid",
|
|
266
|
+
itemNoun: "image",
|
|
267
|
+
hidden: {
|
|
268
|
+
field: "mediaType",
|
|
269
|
+
equals: "gallery"
|
|
270
|
+
},
|
|
271
|
+
validation: {
|
|
272
|
+
min: 2,
|
|
273
|
+
max: 4
|
|
274
|
+
},
|
|
275
|
+
of: [{
|
|
276
|
+
name: "image",
|
|
277
|
+
title: "Image",
|
|
278
|
+
type: "image"
|
|
279
|
+
}]
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "mobileImage",
|
|
283
|
+
title: "Phone image",
|
|
284
|
+
type: "image",
|
|
285
|
+
group: "media",
|
|
286
|
+
description: "Leave empty to reuse the main image."
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "fit",
|
|
290
|
+
title: "Image fit",
|
|
291
|
+
type: "select",
|
|
292
|
+
group: "design",
|
|
293
|
+
layout: "segmented",
|
|
294
|
+
initialValue: "cover",
|
|
295
|
+
options: [{
|
|
296
|
+
value: "cover",
|
|
297
|
+
label: "Fill"
|
|
298
|
+
}, {
|
|
299
|
+
value: "contain",
|
|
300
|
+
label: "Fit",
|
|
301
|
+
description: "For product cut-outs on a plain background"
|
|
302
|
+
}]
|
|
303
|
+
},
|
|
304
|
+
theme,
|
|
305
|
+
spacing
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
value: {
|
|
309
|
+
_variant: "media-right",
|
|
310
|
+
eyebrow: "The Ridge Skillet",
|
|
311
|
+
heading: "Sears like a steakhouse. Weighs a third less.",
|
|
312
|
+
body: "Spun carbon steel, pre-seasoned with flaxseed in Bend and ready for coals, gas or induction on day one.",
|
|
313
|
+
points: [
|
|
314
|
+
"Ready for induction and open fire",
|
|
315
|
+
"Pre-seasoned three times",
|
|
316
|
+
"Lifetime warranty"
|
|
317
|
+
],
|
|
318
|
+
primaryButton: btn("Shop the skillet", "/products/ridge-skillet-10", "primary", { arrow: true }),
|
|
319
|
+
secondaryButton: btn("How we season it", "/journal/seasoning", "link"),
|
|
320
|
+
mediaType: "image",
|
|
321
|
+
image: pic("caldera-ridge-skillet", "Ridge skillet searing two steaks over a bed of coals", 1600, 2e3),
|
|
322
|
+
mobileImage: pic("caldera-ridge-mobile", "Ridge skillet searing two steaks, close crop", 1080, 1350),
|
|
323
|
+
fit: "cover",
|
|
324
|
+
theme: "light",
|
|
325
|
+
spacing: "default"
|
|
326
|
+
},
|
|
327
|
+
notes: {
|
|
328
|
+
why: "The core Hero has one image; real split heroes swap between an image, a muted video and a 2–4 image gallery. One `mediaType` select drives which media fields show, so the content model never holds an image AND a video that disagree. The poster is its own required field because the video type can't require one. The phone crop is a separate optional image, not a crop setting, because art direction on phones is often a different photo. Key points are an array of strings — no icon, no link — so the converter doesn't invent features. Side (left/right/overlap) is a variant; fit, theme and spacing are Design.",
|
|
329
|
+
sanity: "object `mediaHero` { eyebrow, heading, body, points: array<string>, primaryButton/secondaryButton: object button, mediaType: string(list), image: image{hotspot}, video: file|mux.video, poster: image, gallery: array<image>, mobileImage: image } with `hidden: ({parent}) => parent.mediaType !== 'image'`.",
|
|
330
|
+
htmlSignals: [
|
|
331
|
+
"First <section> after the header with a two-column grid: text column (h1, p, 1–2 links) + media column",
|
|
332
|
+
"Media column contains <img>, <picture> with a different mobile <source>, or <video autoplay muted loop playsinline poster>",
|
|
333
|
+
"A short <ul> of 2–3 items with check icons under the paragraph",
|
|
334
|
+
"Column order flipped via flex-direction: row-reverse or order — the media-left variant"
|
|
335
|
+
],
|
|
336
|
+
mcp: "One H1 + paragraph + buttons + a single media column → `mediaHero`. <video> → mediaType 'video', video = src, poster = poster attribute (create an asset from it). Several <img> in the media column → mediaType 'gallery'. <picture> with a (max-width) <source> of a different image → mobileImage. Check-mark list items → points (text only). Media column first in the DOM or row-reverse → variant 'media-left'. object-fit: contain → fit 'contain'.",
|
|
337
|
+
pitfalls: ["A hero with BOTH a background video and a side image is a different section (full-bleed video) — don't merge.", "Alt text on a decorative hero photo should still describe it when it shows the product."]
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "hero-centered-gradient",
|
|
342
|
+
previewLayout: "centered",
|
|
343
|
+
label: "Centered hero with gradient + screenshot",
|
|
344
|
+
proves: "Announcement chip as a link unit · highlighted phrase in a plain string · gradient as design data, not CSS in content",
|
|
345
|
+
category: "Heroes",
|
|
346
|
+
tags: [
|
|
347
|
+
"variants",
|
|
348
|
+
"conditional",
|
|
349
|
+
"validation",
|
|
350
|
+
"media-heavy"
|
|
351
|
+
],
|
|
352
|
+
brand: "Tideline",
|
|
353
|
+
schema: {
|
|
354
|
+
name: "launchHero",
|
|
355
|
+
title: "Launch hero",
|
|
356
|
+
kind: "block",
|
|
357
|
+
icon: "layout-align-center",
|
|
358
|
+
category: "Heroes",
|
|
359
|
+
description: "A centered headline over a gradient, with an optional product screenshot below.",
|
|
360
|
+
variants: [{
|
|
361
|
+
id: "screenshot",
|
|
362
|
+
name: "With screenshot",
|
|
363
|
+
description: "Product UI rises out of the bottom edge"
|
|
364
|
+
}, {
|
|
365
|
+
id: "text",
|
|
366
|
+
name: "Text only"
|
|
367
|
+
}],
|
|
368
|
+
preview: {
|
|
369
|
+
title: "heading",
|
|
370
|
+
subtitle: "chip.label",
|
|
371
|
+
media: "screenshot"
|
|
372
|
+
},
|
|
373
|
+
groups: GROUPS,
|
|
374
|
+
fields: [
|
|
375
|
+
{
|
|
376
|
+
name: "chip",
|
|
377
|
+
title: "Announcement chip",
|
|
378
|
+
type: "object",
|
|
379
|
+
icon: "sparkles",
|
|
380
|
+
openMode: "inline",
|
|
381
|
+
fields: [{
|
|
382
|
+
name: "label",
|
|
383
|
+
title: "Label",
|
|
384
|
+
type: "string",
|
|
385
|
+
validation: { max: 48 },
|
|
386
|
+
placeholder: "New: Traces for Kubernetes"
|
|
387
|
+
}, {
|
|
388
|
+
name: "link",
|
|
389
|
+
title: "Links to",
|
|
390
|
+
type: "link"
|
|
391
|
+
}]
|
|
392
|
+
},
|
|
393
|
+
heading(56),
|
|
394
|
+
{
|
|
395
|
+
name: "highlightedWords",
|
|
396
|
+
title: "Highlighted words",
|
|
397
|
+
type: "string",
|
|
398
|
+
validation: { max: 32 },
|
|
399
|
+
description: "Must match words in the headline exactly. Leave empty for none."
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "subheading",
|
|
403
|
+
title: "Subheading",
|
|
404
|
+
type: "text",
|
|
405
|
+
rows: 2,
|
|
406
|
+
validation: { recommendedMax: 160 },
|
|
407
|
+
ai: true,
|
|
408
|
+
localized: true
|
|
409
|
+
},
|
|
410
|
+
primary,
|
|
411
|
+
secondary,
|
|
412
|
+
{
|
|
413
|
+
name: "note",
|
|
414
|
+
title: "Small print",
|
|
415
|
+
type: "string",
|
|
416
|
+
validation: { max: 64 },
|
|
417
|
+
placeholder: "No credit card required"
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "screenshot",
|
|
421
|
+
title: "Screenshot",
|
|
422
|
+
type: "image",
|
|
423
|
+
group: "media",
|
|
424
|
+
description: "At least 2400px wide."
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: "background",
|
|
428
|
+
title: "Background",
|
|
429
|
+
type: "object",
|
|
430
|
+
group: "design",
|
|
431
|
+
icon: "palette",
|
|
432
|
+
openMode: "drill",
|
|
433
|
+
fields: [
|
|
434
|
+
{
|
|
435
|
+
name: "style",
|
|
436
|
+
title: "Style",
|
|
437
|
+
type: "select",
|
|
438
|
+
layout: "cards",
|
|
439
|
+
initialValue: "radial",
|
|
440
|
+
options: [
|
|
441
|
+
{
|
|
442
|
+
value: "none",
|
|
443
|
+
label: "Plain",
|
|
444
|
+
icon: "square"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
value: "linear",
|
|
448
|
+
label: "Linear",
|
|
449
|
+
icon: "arrows-diagonal"
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
value: "radial",
|
|
453
|
+
label: "Glow",
|
|
454
|
+
icon: "circle-dot",
|
|
455
|
+
description: "A soft light from the top"
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
value: "mesh",
|
|
459
|
+
label: "Mesh",
|
|
460
|
+
icon: "grid-dots",
|
|
461
|
+
description: "Blended color blobs"
|
|
462
|
+
}
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "from",
|
|
467
|
+
title: "Color 1",
|
|
468
|
+
type: "color",
|
|
469
|
+
hidden: {
|
|
470
|
+
field: "style",
|
|
471
|
+
equals: [
|
|
472
|
+
"linear",
|
|
473
|
+
"radial",
|
|
474
|
+
"mesh"
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "to",
|
|
480
|
+
title: "Color 2",
|
|
481
|
+
type: "color",
|
|
482
|
+
hidden: {
|
|
483
|
+
field: "style",
|
|
484
|
+
equals: [
|
|
485
|
+
"linear",
|
|
486
|
+
"radial",
|
|
487
|
+
"mesh"
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "via",
|
|
493
|
+
title: "Color 3",
|
|
494
|
+
type: "color",
|
|
495
|
+
hidden: {
|
|
496
|
+
field: "style",
|
|
497
|
+
equals: "mesh"
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
name: "angle",
|
|
502
|
+
title: "Angle",
|
|
503
|
+
type: "number",
|
|
504
|
+
layout: "slider",
|
|
505
|
+
hidden: {
|
|
506
|
+
field: "style",
|
|
507
|
+
equals: "linear"
|
|
508
|
+
},
|
|
509
|
+
numeric: {
|
|
510
|
+
min: 0,
|
|
511
|
+
max: 360,
|
|
512
|
+
step: 15,
|
|
513
|
+
unit: "°"
|
|
514
|
+
},
|
|
515
|
+
initialValue: 180
|
|
516
|
+
}
|
|
517
|
+
]
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
...theme,
|
|
521
|
+
initialValue: "dark"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "width",
|
|
525
|
+
title: "Text width",
|
|
526
|
+
type: "select",
|
|
527
|
+
group: "design",
|
|
528
|
+
layout: "segmented",
|
|
529
|
+
initialValue: "narrow",
|
|
530
|
+
options: [{
|
|
531
|
+
value: "narrow",
|
|
532
|
+
label: "Narrow"
|
|
533
|
+
}, {
|
|
534
|
+
value: "wide",
|
|
535
|
+
label: "Wide"
|
|
536
|
+
}]
|
|
537
|
+
}
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
value: {
|
|
541
|
+
_variant: "screenshot",
|
|
542
|
+
chip: {
|
|
543
|
+
label: "New: Traces for Kubernetes",
|
|
544
|
+
link: {
|
|
545
|
+
type: "page",
|
|
546
|
+
href: "/changelog/kubernetes-traces",
|
|
547
|
+
title: "Kubernetes traces"
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
heading: "Find the slow service before your customers do.",
|
|
551
|
+
highlightedWords: "slow service",
|
|
552
|
+
subheading: "Tideline links every trace, log and deploy so on-call engineers see the cause in one view — not twelve tabs.",
|
|
553
|
+
primaryButton: btn("Start free", "https://app.tideline.dev/signup", "primary"),
|
|
554
|
+
secondaryButton: btn("Book a demo", "/demo", "outline"),
|
|
555
|
+
note: "Free for 3 engineers · No credit card required",
|
|
556
|
+
screenshot: pic("tideline-service-map", "Tideline service map with the checkout service highlighted in red at 1.8 seconds", 2400, 1500),
|
|
557
|
+
background: {
|
|
558
|
+
style: "radial",
|
|
559
|
+
from: "#1B2A4A",
|
|
560
|
+
to: "#0B1020",
|
|
561
|
+
angle: 180
|
|
562
|
+
},
|
|
563
|
+
theme: "dark",
|
|
564
|
+
width: "narrow"
|
|
565
|
+
},
|
|
566
|
+
notes: {
|
|
567
|
+
why: "The gradient is DESIGN data: a style select plus colors in the Design group, so a rebrand changes three values instead of rewriting CSS strings pasted into content. Which color fields show depends on the style (mesh needs three, plain needs none). The highlighted phrase is a separate string that must match part of the headline — a pragmatic stand-in for inline marks in a single-line field (richtext would let authors add bold, links and line breaks we don't want in an H1). The announcement chip is an inline object of label + link because it's a link with its own copy, not a button.",
|
|
568
|
+
sanity: "object `launchHero` { chip: object { label, link }, heading: string, highlight: string, subheading: text, primary/secondary, note, screenshot: image, background: object { style, from/via/to: color (sanity-plugin-color-input), angle: number } }. Many Sanity teams model the heading as a tiny Portable Text with a single 'highlight' decorator instead.",
|
|
569
|
+
htmlSignals: [
|
|
570
|
+
"A centered <section> (text-align:center, max-width ~720px) with a pill-shaped <a> above the H1",
|
|
571
|
+
"A <span> inside the H1 with a gradient/color class — the highlighted phrase",
|
|
572
|
+
"background-image: linear-gradient/radial-gradient on the section, or absolutely positioned blurred blobs (mesh)",
|
|
573
|
+
"A large <img> of app UI below the buttons, often with a perspective transform or bottom fade",
|
|
574
|
+
"A small muted line under the buttons ('No credit card required')"
|
|
575
|
+
],
|
|
576
|
+
mcp: "Centered H1 + optional pill link → `launchHero`. Pill → chip { label: text, link: href }. A <span>/<em> inside the H1 → highlightedWords = its text; heading = the H1's full plain text. Parse the section's gradient: linear-gradient(angle, a, b) → { style: 'linear', angle, from: a, to: b }; radial-gradient → 'radial'; blurred absolutely-positioned blobs → 'mesh' with up to three colors. Snap colors to the nearest brand token when one is within ΔE 5 (report the rest). Screenshot <img> → screenshot; variant 'screenshot' when present else 'text'.",
|
|
577
|
+
pitfalls: ["Editing the headline can orphan the highlight — the renderer must ignore a highlight that no longer matches (no cross-field validation yet).", "Don't store raw CSS gradient strings — they can't be re-themed or validated."]
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
id: "hero-video-fullbleed",
|
|
582
|
+
previewLayout: "media",
|
|
583
|
+
label: "Full-bleed video hero with captions",
|
|
584
|
+
proves: "An Accessibility tab: caption tracks, transcript, text alternative · poster required · pause control locked on",
|
|
585
|
+
category: "Heroes",
|
|
586
|
+
tags: [
|
|
587
|
+
"media-heavy",
|
|
588
|
+
"a11y",
|
|
589
|
+
"conditional",
|
|
590
|
+
"repeater",
|
|
591
|
+
"validation"
|
|
592
|
+
],
|
|
593
|
+
brand: "Open Water Trust",
|
|
594
|
+
schema: {
|
|
595
|
+
name: "videoHero",
|
|
596
|
+
title: "Video hero",
|
|
597
|
+
kind: "block",
|
|
598
|
+
icon: "movie",
|
|
599
|
+
category: "Heroes",
|
|
600
|
+
description: "A muted background film behind a headline, with captions and a transcript.",
|
|
601
|
+
preview: {
|
|
602
|
+
title: "heading",
|
|
603
|
+
subtitle: "eyebrow",
|
|
604
|
+
media: "poster"
|
|
605
|
+
},
|
|
606
|
+
groups: [
|
|
607
|
+
{
|
|
608
|
+
name: "content",
|
|
609
|
+
title: "Content",
|
|
610
|
+
default: true
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: "media",
|
|
614
|
+
title: "Media"
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
name: "a11y",
|
|
618
|
+
title: "Accessibility"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: "design",
|
|
622
|
+
title: "Design"
|
|
623
|
+
}
|
|
624
|
+
],
|
|
625
|
+
fields: [
|
|
626
|
+
eyebrow,
|
|
627
|
+
heading(56),
|
|
628
|
+
body(140),
|
|
629
|
+
primary,
|
|
630
|
+
{
|
|
631
|
+
...secondary,
|
|
632
|
+
description: "Often links to the full film with sound, e.g. Watch the 4-minute film."
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "video",
|
|
636
|
+
title: "Background video",
|
|
637
|
+
type: "video",
|
|
638
|
+
group: "media",
|
|
639
|
+
validation: { required: true },
|
|
640
|
+
description: "MP4 or a Vimeo link, under 10 MB."
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
name: "mobileVideo",
|
|
644
|
+
title: "Phone video",
|
|
645
|
+
type: "video",
|
|
646
|
+
group: "media",
|
|
647
|
+
description: "Leave empty to show the poster on phones."
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
name: "poster",
|
|
651
|
+
title: "Poster",
|
|
652
|
+
type: "image",
|
|
653
|
+
group: "media",
|
|
654
|
+
validation: { required: true }
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
name: "captions",
|
|
658
|
+
title: "Caption tracks",
|
|
659
|
+
type: "array",
|
|
660
|
+
group: "a11y",
|
|
661
|
+
itemNoun: "track",
|
|
662
|
+
validation: { max: 6 },
|
|
663
|
+
description: "Required when the video has speech.",
|
|
664
|
+
of: [{
|
|
665
|
+
name: "track",
|
|
666
|
+
title: "Track",
|
|
667
|
+
type: "object",
|
|
668
|
+
icon: "subtask",
|
|
669
|
+
openMode: "inline",
|
|
670
|
+
preview: {
|
|
671
|
+
title: "label",
|
|
672
|
+
subtitle: "language",
|
|
673
|
+
fallback: "Untitled track"
|
|
674
|
+
},
|
|
675
|
+
fields: [
|
|
676
|
+
{
|
|
677
|
+
name: "language",
|
|
678
|
+
title: "Language",
|
|
679
|
+
type: "select",
|
|
680
|
+
validation: {
|
|
681
|
+
required: true,
|
|
682
|
+
unique: true
|
|
683
|
+
},
|
|
684
|
+
options: [
|
|
685
|
+
{
|
|
686
|
+
value: "en",
|
|
687
|
+
label: "English"
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
value: "es",
|
|
691
|
+
label: "Spanish"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
value: "fr",
|
|
695
|
+
label: "French"
|
|
696
|
+
}
|
|
697
|
+
]
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
name: "label",
|
|
701
|
+
title: "Menu label",
|
|
702
|
+
type: "string",
|
|
703
|
+
validation: {
|
|
704
|
+
required: true,
|
|
705
|
+
max: 24
|
|
706
|
+
},
|
|
707
|
+
placeholder: "English (CC)"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
name: "file",
|
|
711
|
+
title: "Caption file",
|
|
712
|
+
type: "file",
|
|
713
|
+
validation: { required: true },
|
|
714
|
+
description: "A .vtt file."
|
|
715
|
+
}
|
|
716
|
+
]
|
|
717
|
+
}]
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
name: "textAlternative",
|
|
721
|
+
title: "What the video shows",
|
|
722
|
+
type: "text",
|
|
723
|
+
group: "a11y",
|
|
724
|
+
rows: 2,
|
|
725
|
+
validation: {
|
|
726
|
+
required: true,
|
|
727
|
+
recommendedMax: 200
|
|
728
|
+
},
|
|
729
|
+
description: "Not shown. Read by screen readers instead of the video."
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
name: "transcript",
|
|
733
|
+
title: "Transcript",
|
|
734
|
+
type: "link",
|
|
735
|
+
group: "a11y"
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
name: "showPauseButton",
|
|
739
|
+
title: "Show pause button",
|
|
740
|
+
type: "boolean",
|
|
741
|
+
group: "a11y",
|
|
742
|
+
initialValue: true,
|
|
743
|
+
access: { edit: "schema.manage" },
|
|
744
|
+
lockReason: "Moving video over 5 seconds must be pausable (WCAG 2.2.2)."
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: "autoplay",
|
|
748
|
+
title: "Play automatically",
|
|
749
|
+
type: "boolean",
|
|
750
|
+
group: "design",
|
|
751
|
+
initialValue: true
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: "loop",
|
|
755
|
+
title: "Loop the video",
|
|
756
|
+
type: "boolean",
|
|
757
|
+
group: "design",
|
|
758
|
+
hidden: {
|
|
759
|
+
field: "autoplay",
|
|
760
|
+
equals: true
|
|
761
|
+
},
|
|
762
|
+
initialValue: true
|
|
763
|
+
},
|
|
764
|
+
overlay,
|
|
765
|
+
{
|
|
766
|
+
name: "height",
|
|
767
|
+
title: "Height",
|
|
768
|
+
type: "select",
|
|
769
|
+
group: "design",
|
|
770
|
+
layout: "segmented",
|
|
771
|
+
initialValue: "screen",
|
|
772
|
+
options: [
|
|
773
|
+
{
|
|
774
|
+
value: "screen",
|
|
775
|
+
label: "Full screen"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
value: "large",
|
|
779
|
+
label: "Large"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
value: "medium",
|
|
783
|
+
label: "Medium"
|
|
784
|
+
}
|
|
785
|
+
]
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
...theme,
|
|
789
|
+
initialValue: "dark"
|
|
790
|
+
}
|
|
791
|
+
]
|
|
792
|
+
},
|
|
793
|
+
value: {
|
|
794
|
+
eyebrow: "Klamath River · 2026",
|
|
795
|
+
heading: "The dams came down. The salmon came back.",
|
|
796
|
+
body: "Four dams removed, 400 river miles reopened. Your gift keeps the Klamath running free.",
|
|
797
|
+
primaryButton: btn("Donate", "/donate", "primary"),
|
|
798
|
+
secondaryButton: btn("Watch the 4-minute film", "/stories/klamath-film", "outline", { icon: "player-play" }),
|
|
799
|
+
video: {
|
|
800
|
+
url: "https://vimeo.com/883441207",
|
|
801
|
+
poster: "https://picsum.photos/seed/owt-klamath-poster/1920/1080"
|
|
802
|
+
},
|
|
803
|
+
poster: pic("owt-klamath-poster", "Chinook salmon swimming upstream through clear water over red gravel", 1920, 1080),
|
|
804
|
+
captions: [{
|
|
805
|
+
_key: "t1",
|
|
806
|
+
language: "en",
|
|
807
|
+
label: "English (CC)",
|
|
808
|
+
file: {
|
|
809
|
+
name: "klamath-hero-en.vtt",
|
|
810
|
+
size: 2140,
|
|
811
|
+
url: "https://cdn.bettercms.site/owt/klamath-hero-en.vtt"
|
|
812
|
+
}
|
|
813
|
+
}, {
|
|
814
|
+
_key: "t2",
|
|
815
|
+
language: "es",
|
|
816
|
+
label: "Español",
|
|
817
|
+
file: {
|
|
818
|
+
name: "klamath-hero-es.vtt",
|
|
819
|
+
size: 2296,
|
|
820
|
+
url: "https://cdn.bettercms.site/owt/klamath-hero-es.vtt"
|
|
821
|
+
}
|
|
822
|
+
}],
|
|
823
|
+
textAlternative: "Salmon swim upstream past the old Iron Gate dam site, now a free-flowing river lined with new willows.",
|
|
824
|
+
transcript: {
|
|
825
|
+
type: "page",
|
|
826
|
+
href: "/stories/klamath-film/transcript",
|
|
827
|
+
title: "Klamath film transcript"
|
|
828
|
+
},
|
|
829
|
+
showPauseButton: true,
|
|
830
|
+
autoplay: true,
|
|
831
|
+
loop: true,
|
|
832
|
+
overlay: 45,
|
|
833
|
+
height: "screen",
|
|
834
|
+
theme: "dark"
|
|
835
|
+
},
|
|
836
|
+
notes: {
|
|
837
|
+
why: "Video heroes fail accessibility more than any other section, so accessibility gets its own tab instead of being buried. Caption tracks are an array (one per language: language + menu label + .vtt file) because the `video` type can't carry tracks itself. A text alternative is required — a muted background film still communicates. The pause control is locked on for marketers with the WCAG reason shown. The poster is required: it's what people with reduced motion and slow connections actually see. Autoplay/loop/overlay/height are Design; loop only appears when autoplay is on.",
|
|
838
|
+
sanity: "object `videoHero` { video: mux.video | file, poster: image (required), captions: array<object { language, label, file: file{accept:'.vtt'} }>, textAlternative: text, transcript: link } — Mux stores text tracks on the asset itself, which Sanity surfaces via the Mux plugin.",
|
|
839
|
+
htmlSignals: [
|
|
840
|
+
"<video autoplay muted loop playsinline poster=…> absolutely positioned behind the H1 (object-fit: cover)",
|
|
841
|
+
"<track kind=\"captions\" srclang=… src=….vtt> children (often missing — report it)",
|
|
842
|
+
"A pause/play <button aria-label=\"Pause video\"> overlaid in a corner",
|
|
843
|
+
"@media (prefers-reduced-motion) rule hiding the video or pausing it",
|
|
844
|
+
"A gradient/black overlay div between video and text"
|
|
845
|
+
],
|
|
846
|
+
mcp: "Background <video> behind text → `videoHero`. src (or the highest-quality <source>) → video.url; poster attribute → poster (upload as an asset; if missing, grab the first frame and report). Each <track> → captions[] { language: srclang, label, file }. If there is no <track> and the film has speech, report an a11y gap — don't invent captions. aria-label/description near the video → textAlternative; else draft one with AI and mark it for review. Overlay opacity → overlay %.",
|
|
847
|
+
pitfalls: ["A YouTube iframe can't autoplay muted reliably as a background — prefer MP4/Vimeo background mode; flag YouTube sources.", "Captions and transcript are different things: captions sync to the film, a transcript is a page. Model both."]
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
id: "hero-slider",
|
|
852
|
+
previewLayout: "carousel",
|
|
853
|
+
label: "Slider hero",
|
|
854
|
+
proves: "Slides array with per-slide schedule and art direction · autoplay settings only when autoplay is on · carousel a11y name",
|
|
855
|
+
category: "Heroes",
|
|
856
|
+
tags: [
|
|
857
|
+
"repeater",
|
|
858
|
+
"media-heavy",
|
|
859
|
+
"conditional",
|
|
860
|
+
"validation",
|
|
861
|
+
"a11y"
|
|
862
|
+
],
|
|
863
|
+
brand: "Caldera",
|
|
864
|
+
schema: {
|
|
865
|
+
name: "heroCarousel",
|
|
866
|
+
title: "Hero carousel",
|
|
867
|
+
kind: "block",
|
|
868
|
+
icon: "slideshow",
|
|
869
|
+
category: "Heroes",
|
|
870
|
+
description: "Two to five full-width slides, each with its own message and button.",
|
|
871
|
+
preview: {
|
|
872
|
+
title: "slides.0.heading",
|
|
873
|
+
subtitle: "label",
|
|
874
|
+
media: "slides.0.image"
|
|
875
|
+
},
|
|
876
|
+
groups: [
|
|
877
|
+
{
|
|
878
|
+
name: "content",
|
|
879
|
+
title: "Slides",
|
|
880
|
+
default: true
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
name: "behavior",
|
|
884
|
+
title: "Behavior"
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
name: "design",
|
|
888
|
+
title: "Design"
|
|
889
|
+
}
|
|
890
|
+
],
|
|
891
|
+
fields: [
|
|
892
|
+
{
|
|
893
|
+
name: "slides",
|
|
894
|
+
title: "Slides",
|
|
895
|
+
type: "array",
|
|
896
|
+
itemNoun: "slide",
|
|
897
|
+
validation: {
|
|
898
|
+
min: 2,
|
|
899
|
+
max: 5
|
|
900
|
+
},
|
|
901
|
+
of: [{
|
|
902
|
+
name: "slide",
|
|
903
|
+
title: "Slide",
|
|
904
|
+
type: "object",
|
|
905
|
+
icon: "photo",
|
|
906
|
+
preview: {
|
|
907
|
+
title: "heading",
|
|
908
|
+
subtitle: "eyebrow",
|
|
909
|
+
media: "image",
|
|
910
|
+
fallback: "Untitled slide"
|
|
911
|
+
},
|
|
912
|
+
fields: [
|
|
913
|
+
eyebrow,
|
|
914
|
+
{
|
|
915
|
+
name: "heading",
|
|
916
|
+
title: "Heading",
|
|
917
|
+
type: "string",
|
|
918
|
+
validation: {
|
|
919
|
+
required: true,
|
|
920
|
+
max: 80,
|
|
921
|
+
recommendedMax: 50
|
|
922
|
+
},
|
|
923
|
+
ai: true,
|
|
924
|
+
localized: true
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
name: "body",
|
|
928
|
+
title: "Body",
|
|
929
|
+
type: "text",
|
|
930
|
+
rows: 2,
|
|
931
|
+
validation: { recommendedMax: 120 },
|
|
932
|
+
localized: true
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
name: "button",
|
|
936
|
+
title: "Button",
|
|
937
|
+
type: "button",
|
|
938
|
+
validation: { required: true }
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
name: "image",
|
|
942
|
+
title: "Image",
|
|
943
|
+
type: "image",
|
|
944
|
+
validation: { required: true },
|
|
945
|
+
description: "At least 2400 × 1200."
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: "mobileImage",
|
|
949
|
+
title: "Phone image",
|
|
950
|
+
type: "image",
|
|
951
|
+
description: "Leave empty to reuse the main image."
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: "textPosition",
|
|
955
|
+
title: "Text position",
|
|
956
|
+
type: "select",
|
|
957
|
+
layout: "segmented",
|
|
958
|
+
initialValue: "left",
|
|
959
|
+
options: [
|
|
960
|
+
{
|
|
961
|
+
value: "left",
|
|
962
|
+
label: "Left"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
value: "center",
|
|
966
|
+
label: "Center"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
value: "right",
|
|
970
|
+
label: "Right"
|
|
971
|
+
}
|
|
972
|
+
]
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
name: "tone",
|
|
976
|
+
title: "Text color",
|
|
977
|
+
type: "select",
|
|
978
|
+
layout: "segmented",
|
|
979
|
+
initialValue: "light",
|
|
980
|
+
options: [{
|
|
981
|
+
value: "light",
|
|
982
|
+
label: "Light text"
|
|
983
|
+
}, {
|
|
984
|
+
value: "dark",
|
|
985
|
+
label: "Dark text"
|
|
986
|
+
}]
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
name: "showFrom",
|
|
990
|
+
title: "Show from",
|
|
991
|
+
type: "datetime",
|
|
992
|
+
fieldset: "Schedule"
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
name: "hideAfter",
|
|
996
|
+
title: "Hide after",
|
|
997
|
+
type: "datetime",
|
|
998
|
+
fieldset: "Schedule"
|
|
999
|
+
}
|
|
1000
|
+
]
|
|
1001
|
+
}]
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
name: "label",
|
|
1005
|
+
title: "Carousel name",
|
|
1006
|
+
type: "string",
|
|
1007
|
+
validation: {
|
|
1008
|
+
required: true,
|
|
1009
|
+
max: 40
|
|
1010
|
+
},
|
|
1011
|
+
description: "Not shown. Read by screen readers."
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
name: "autoplay",
|
|
1015
|
+
title: "Play automatically",
|
|
1016
|
+
type: "boolean",
|
|
1017
|
+
group: "behavior",
|
|
1018
|
+
initialValue: true
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
name: "interval",
|
|
1022
|
+
title: "Each slide shows for",
|
|
1023
|
+
type: "number",
|
|
1024
|
+
group: "behavior",
|
|
1025
|
+
hidden: {
|
|
1026
|
+
field: "autoplay",
|
|
1027
|
+
equals: true
|
|
1028
|
+
},
|
|
1029
|
+
numeric: {
|
|
1030
|
+
min: 4,
|
|
1031
|
+
max: 12,
|
|
1032
|
+
step: 1,
|
|
1033
|
+
unit: "s"
|
|
1034
|
+
},
|
|
1035
|
+
initialValue: 6
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: "transition",
|
|
1039
|
+
title: "Transition",
|
|
1040
|
+
type: "select",
|
|
1041
|
+
group: "design",
|
|
1042
|
+
layout: "segmented",
|
|
1043
|
+
initialValue: "fade",
|
|
1044
|
+
options: [{
|
|
1045
|
+
value: "fade",
|
|
1046
|
+
label: "Fade"
|
|
1047
|
+
}, {
|
|
1048
|
+
value: "slide",
|
|
1049
|
+
label: "Slide"
|
|
1050
|
+
}]
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
name: "controls",
|
|
1054
|
+
title: "Controls",
|
|
1055
|
+
type: "multiselect",
|
|
1056
|
+
group: "design",
|
|
1057
|
+
initialValue: ["dots", "arrows"],
|
|
1058
|
+
options: [
|
|
1059
|
+
{
|
|
1060
|
+
value: "dots",
|
|
1061
|
+
label: "Dots"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
value: "arrows",
|
|
1065
|
+
label: "Arrows"
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
value: "progress",
|
|
1069
|
+
label: "Progress bar"
|
|
1070
|
+
}
|
|
1071
|
+
]
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: "height",
|
|
1075
|
+
title: "Height",
|
|
1076
|
+
type: "select",
|
|
1077
|
+
group: "design",
|
|
1078
|
+
layout: "segmented",
|
|
1079
|
+
initialValue: "large",
|
|
1080
|
+
options: [
|
|
1081
|
+
{
|
|
1082
|
+
value: "screen",
|
|
1083
|
+
label: "Full screen"
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
value: "large",
|
|
1087
|
+
label: "Large"
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
value: "medium",
|
|
1091
|
+
label: "Medium"
|
|
1092
|
+
}
|
|
1093
|
+
]
|
|
1094
|
+
}
|
|
1095
|
+
]
|
|
1096
|
+
},
|
|
1097
|
+
value: {
|
|
1098
|
+
label: "This season at Caldera",
|
|
1099
|
+
slides: [
|
|
1100
|
+
{
|
|
1101
|
+
_key: "s1",
|
|
1102
|
+
eyebrow: "New",
|
|
1103
|
+
heading: "The Cable Fold Grill folds to 2 cm.",
|
|
1104
|
+
body: "A full grill grate that rides in a backpack and holds a 25 kg Dutch oven.",
|
|
1105
|
+
button: btn("Shop the Fold", "/products/cable-fold", "primary", { arrow: true }),
|
|
1106
|
+
image: pic("caldera-slide-fold", "Cable Fold Grill over a riverside fire at dusk", 2400, 1200),
|
|
1107
|
+
mobileImage: pic("caldera-slide-fold-m", "Cable Fold Grill over coals, close crop", 1080, 1350),
|
|
1108
|
+
textPosition: "left",
|
|
1109
|
+
tone: "light"
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
_key: "s2",
|
|
1113
|
+
eyebrow: "Black Friday",
|
|
1114
|
+
heading: "25% off every Dutch oven.",
|
|
1115
|
+
button: btn("Shop the sale", "/collections/sale", "primary"),
|
|
1116
|
+
image: pic("caldera-slide-sale", "Three enamelled Dutch ovens in rust, sage and slate", 2400, 1200),
|
|
1117
|
+
textPosition: "center",
|
|
1118
|
+
tone: "dark",
|
|
1119
|
+
showFrom: "2026-11-26T00:00:00-08:00[America/Los_Angeles]",
|
|
1120
|
+
hideAfter: "2026-11-30T23:59:00-08:00[America/Los_Angeles]"
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
_key: "s3",
|
|
1124
|
+
eyebrow: "Recipes",
|
|
1125
|
+
heading: "Skillet cornbread, over coals.",
|
|
1126
|
+
body: "Twenty minutes, one pan, and a crust you only get from cast iron.",
|
|
1127
|
+
button: btn("Get the recipe", "/recipes/skillet-cornbread", "outline"),
|
|
1128
|
+
image: pic("caldera-slide-cornbread", "Golden cornbread in a cast iron skillet on a camp table", 2400, 1200),
|
|
1129
|
+
textPosition: "right",
|
|
1130
|
+
tone: "light"
|
|
1131
|
+
}
|
|
1132
|
+
],
|
|
1133
|
+
autoplay: true,
|
|
1134
|
+
interval: 6,
|
|
1135
|
+
transition: "fade",
|
|
1136
|
+
controls: ["dots", "arrows"],
|
|
1137
|
+
height: "large"
|
|
1138
|
+
},
|
|
1139
|
+
notes: {
|
|
1140
|
+
why: "Slides are an array of objects with the same shape (each with ONE button — a slide with two CTAs is a sign it should be its own hero). Per-slide text position and text color live on the slide because they depend on that photo; section-level behavior lives in Behavior/Design. Each slide carries an optional schedule so a sale slide can appear and vanish without a republish. `interval` only shows when autoplay is on. The carousel name is required content because screen readers announce it ('Featured collections, carousel').",
|
|
1141
|
+
sanity: "object `heroCarousel` { slides: array<object slide { eyebrow, heading, body, button, image, mobileImage, textPosition, tone, showFrom, hideAfter }> (min 2, max 5), label: string, autoplay: boolean, interval: number (hidden unless autoplay) }.",
|
|
1142
|
+
htmlSignals: [
|
|
1143
|
+
"Swiper/Splide/Slick/Flickity class names (swiper-slide, splide__slide, slick-slide) or [aria-roledescription=\"carousel\"]",
|
|
1144
|
+
"2+ full-width slides, each with its own H2/H1, paragraph, button and background image",
|
|
1145
|
+
"Dots (<button aria-label=\"Go to slide 2\">) and prev/next arrows",
|
|
1146
|
+
"data-autoplay / autoplay: { delay: 6000 } in the init script"
|
|
1147
|
+
],
|
|
1148
|
+
mcp: "Carousel library markup at the top of the page → `heroCarousel`. Each non-cloned slide → slides[] (skip duplicates the library clones for looping: .swiper-slide-duplicate, .slick-cloned). Only the first slide's heading should be an H1 in the renderer. Read autoplay delay (ms → s) into interval; fade effect → transition 'fade'. Background images per slide → image; mobile <source> → mobileImage. Missing aria-label on the carousel → draft label from the slides' theme and report.",
|
|
1149
|
+
pitfalls: ["Cloned slides doubling the count — dedupe by image URL + heading.", "Slide schedules that leave fewer than 2 live slides: the renderer should fall back to a static hero (min can't be enforced over time)."]
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
id: "hero-product",
|
|
1154
|
+
previewLayout: "split",
|
|
1155
|
+
label: "Product hero with variant picker",
|
|
1156
|
+
proves: "Live commerce data through a reference · overrides that fall back to the product · Add to cart is an action label",
|
|
1157
|
+
category: "Heroes",
|
|
1158
|
+
tags: [
|
|
1159
|
+
"references",
|
|
1160
|
+
"commerce",
|
|
1161
|
+
"media-heavy",
|
|
1162
|
+
"conditional",
|
|
1163
|
+
"repeater"
|
|
1164
|
+
],
|
|
1165
|
+
brand: "Caldera",
|
|
1166
|
+
schema: {
|
|
1167
|
+
name: "productHero",
|
|
1168
|
+
title: "Product hero",
|
|
1169
|
+
kind: "block",
|
|
1170
|
+
icon: "shopping-bag",
|
|
1171
|
+
category: "Heroes",
|
|
1172
|
+
description: "One product with its gallery, options, live price and an add-to-cart button.",
|
|
1173
|
+
variants: [
|
|
1174
|
+
{
|
|
1175
|
+
id: "gallery-left",
|
|
1176
|
+
name: "Gallery left"
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
id: "gallery-right",
|
|
1180
|
+
name: "Gallery right"
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
id: "stacked",
|
|
1184
|
+
name: "Stacked",
|
|
1185
|
+
description: "Large image on top, buy box below"
|
|
1186
|
+
}
|
|
1187
|
+
],
|
|
1188
|
+
preview: {
|
|
1189
|
+
title: "heading",
|
|
1190
|
+
subtitle: "badge",
|
|
1191
|
+
media: "gallery.0"
|
|
1192
|
+
},
|
|
1193
|
+
groups: [
|
|
1194
|
+
{
|
|
1195
|
+
name: "content",
|
|
1196
|
+
title: "Content",
|
|
1197
|
+
default: true
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
name: "buy",
|
|
1201
|
+
title: "Buy box"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
name: "media",
|
|
1205
|
+
title: "Media"
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
name: "design",
|
|
1209
|
+
title: "Design"
|
|
1210
|
+
}
|
|
1211
|
+
],
|
|
1212
|
+
fields: [
|
|
1213
|
+
{
|
|
1214
|
+
name: "product",
|
|
1215
|
+
title: "Product",
|
|
1216
|
+
type: "reference",
|
|
1217
|
+
reference: { to: ["shopify"] },
|
|
1218
|
+
validation: { required: true }
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
name: "heading",
|
|
1222
|
+
title: "Heading",
|
|
1223
|
+
type: "string",
|
|
1224
|
+
validation: { max: 80 },
|
|
1225
|
+
ai: true,
|
|
1226
|
+
description: "Leave empty to use the product name."
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
name: "tagline",
|
|
1230
|
+
title: "Tagline",
|
|
1231
|
+
type: "text",
|
|
1232
|
+
rows: 2,
|
|
1233
|
+
validation: { recommendedMax: 140 },
|
|
1234
|
+
ai: true
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
name: "badge",
|
|
1238
|
+
title: "Badge",
|
|
1239
|
+
type: "string",
|
|
1240
|
+
validation: { max: 16 },
|
|
1241
|
+
placeholder: "New color"
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
name: "highlights",
|
|
1245
|
+
title: "Highlights",
|
|
1246
|
+
type: "array",
|
|
1247
|
+
itemNoun: "highlight",
|
|
1248
|
+
openMode: "inline",
|
|
1249
|
+
validation: { max: 4 },
|
|
1250
|
+
of: [{
|
|
1251
|
+
name: "highlight",
|
|
1252
|
+
title: "Highlight",
|
|
1253
|
+
type: "object",
|
|
1254
|
+
icon: "circle-check",
|
|
1255
|
+
preview: {
|
|
1256
|
+
title: "label",
|
|
1257
|
+
fallback: "Untitled highlight"
|
|
1258
|
+
},
|
|
1259
|
+
fields: [{
|
|
1260
|
+
name: "icon",
|
|
1261
|
+
title: "Icon",
|
|
1262
|
+
type: "icon"
|
|
1263
|
+
}, {
|
|
1264
|
+
name: "label",
|
|
1265
|
+
title: "Label",
|
|
1266
|
+
type: "string",
|
|
1267
|
+
validation: {
|
|
1268
|
+
required: true,
|
|
1269
|
+
max: 40
|
|
1270
|
+
}
|
|
1271
|
+
}]
|
|
1272
|
+
}]
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
name: "pickers",
|
|
1276
|
+
title: "Option pickers",
|
|
1277
|
+
type: "multiselect",
|
|
1278
|
+
group: "buy",
|
|
1279
|
+
description: "Options left out use their default.",
|
|
1280
|
+
options: [
|
|
1281
|
+
{
|
|
1282
|
+
value: "color",
|
|
1283
|
+
label: "Color"
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
value: "size",
|
|
1287
|
+
label: "Size"
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
value: "finish",
|
|
1291
|
+
label: "Finish"
|
|
1292
|
+
}
|
|
1293
|
+
]
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
name: "swatchStyle",
|
|
1297
|
+
title: "Color picker",
|
|
1298
|
+
type: "select",
|
|
1299
|
+
group: "buy",
|
|
1300
|
+
layout: "segmented",
|
|
1301
|
+
initialValue: "swatch",
|
|
1302
|
+
options: [{
|
|
1303
|
+
value: "swatch",
|
|
1304
|
+
label: "Swatches"
|
|
1305
|
+
}, {
|
|
1306
|
+
value: "dropdown",
|
|
1307
|
+
label: "Dropdown"
|
|
1308
|
+
}]
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
name: "price",
|
|
1312
|
+
title: "Price",
|
|
1313
|
+
type: "select",
|
|
1314
|
+
group: "buy",
|
|
1315
|
+
layout: "segmented",
|
|
1316
|
+
initialValue: "full",
|
|
1317
|
+
options: [
|
|
1318
|
+
{
|
|
1319
|
+
value: "full",
|
|
1320
|
+
label: "Show price"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
value: "from",
|
|
1324
|
+
label: "From $…"
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
value: "hidden",
|
|
1328
|
+
label: "Hide"
|
|
1329
|
+
}
|
|
1330
|
+
]
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
name: "showCompareAtPrice",
|
|
1334
|
+
title: "Show compare-at price",
|
|
1335
|
+
type: "boolean",
|
|
1336
|
+
group: "buy",
|
|
1337
|
+
hidden: {
|
|
1338
|
+
field: "price",
|
|
1339
|
+
equals: "full"
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
name: "showInstallments",
|
|
1344
|
+
title: "Show pay-later line",
|
|
1345
|
+
type: "boolean",
|
|
1346
|
+
group: "buy",
|
|
1347
|
+
hidden: {
|
|
1348
|
+
field: "price",
|
|
1349
|
+
equals: "full"
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
name: "addLabel",
|
|
1354
|
+
title: "Add to cart label",
|
|
1355
|
+
type: "string",
|
|
1356
|
+
group: "buy",
|
|
1357
|
+
initialValue: "Add to cart",
|
|
1358
|
+
validation: {
|
|
1359
|
+
required: true,
|
|
1360
|
+
max: 24
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
name: "soldOutLabel",
|
|
1365
|
+
title: "Sold out label",
|
|
1366
|
+
type: "string",
|
|
1367
|
+
group: "buy",
|
|
1368
|
+
initialValue: "Notify me",
|
|
1369
|
+
validation: { max: 24 }
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
name: "detailsButton",
|
|
1373
|
+
title: "Details button",
|
|
1374
|
+
type: "button",
|
|
1375
|
+
group: "buy"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
name: "showRating",
|
|
1379
|
+
title: "Show rating",
|
|
1380
|
+
type: "boolean",
|
|
1381
|
+
group: "buy",
|
|
1382
|
+
initialValue: true
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
name: "gallery",
|
|
1386
|
+
title: "Gallery",
|
|
1387
|
+
type: "array",
|
|
1388
|
+
group: "media",
|
|
1389
|
+
arrayLayout: "grid",
|
|
1390
|
+
itemNoun: "image",
|
|
1391
|
+
validation: { max: 8 },
|
|
1392
|
+
description: "Leave empty to use the product's photos.",
|
|
1393
|
+
of: [{
|
|
1394
|
+
name: "image",
|
|
1395
|
+
title: "Image",
|
|
1396
|
+
type: "image"
|
|
1397
|
+
}]
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
name: "video",
|
|
1401
|
+
title: "Video",
|
|
1402
|
+
type: "video",
|
|
1403
|
+
group: "media"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
name: "isBuyBoxSticky",
|
|
1407
|
+
title: "Keep the buy box in view",
|
|
1408
|
+
type: "boolean",
|
|
1409
|
+
group: "design",
|
|
1410
|
+
initialValue: true
|
|
1411
|
+
},
|
|
1412
|
+
theme
|
|
1413
|
+
]
|
|
1414
|
+
},
|
|
1415
|
+
value: {
|
|
1416
|
+
_variant: "gallery-left",
|
|
1417
|
+
product: "gid://shopify/Product/8813",
|
|
1418
|
+
heading: "",
|
|
1419
|
+
tagline: "Enamelled cast iron that goes from coals to table. Holds heat for an hour after it leaves the fire.",
|
|
1420
|
+
badge: "New colour",
|
|
1421
|
+
highlights: [
|
|
1422
|
+
{
|
|
1423
|
+
_key: "h1",
|
|
1424
|
+
icon: "flame",
|
|
1425
|
+
label: "Safe to 500°F, lid knob included"
|
|
1426
|
+
},
|
|
1427
|
+
{
|
|
1428
|
+
_key: "h2",
|
|
1429
|
+
icon: "scale",
|
|
1430
|
+
label: "5.5 quarts · feeds 6"
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
_key: "h3",
|
|
1434
|
+
icon: "shield-check",
|
|
1435
|
+
label: "Lifetime warranty"
|
|
1436
|
+
}
|
|
1437
|
+
],
|
|
1438
|
+
pickers: ["color", "size"],
|
|
1439
|
+
swatchStyle: "swatch",
|
|
1440
|
+
price: "full",
|
|
1441
|
+
showCompareAtPrice: true,
|
|
1442
|
+
showInstallments: true,
|
|
1443
|
+
addLabel: "Add to cart",
|
|
1444
|
+
soldOutLabel: "Notify me",
|
|
1445
|
+
detailsButton: btn("See full details", "/products/ember-dutch-oven", "link", { arrow: true }),
|
|
1446
|
+
showRating: true,
|
|
1447
|
+
gallery: [
|
|
1448
|
+
pic("caldera-ember-rust", "Ember Dutch Oven in rust enamel, lid on", 1600, 1600),
|
|
1449
|
+
pic("caldera-ember-coals", "Ember Dutch Oven sitting in campfire coals with stew bubbling", 1600, 1600),
|
|
1450
|
+
pic("caldera-ember-sage", "Ember Dutch Oven in sage enamel on a wooden table", 1600, 1600)
|
|
1451
|
+
],
|
|
1452
|
+
isBuyBoxSticky: true,
|
|
1453
|
+
theme: "light"
|
|
1454
|
+
},
|
|
1455
|
+
notes: {
|
|
1456
|
+
why: "Everything that changes with stock and price is a REFERENCE to the commerce product — the hero stores only editorial choices: an optional headline override, tagline, badge, highlights, and HOW to present the buy box (which option pickers, price style). Override fields say 'leave empty to use the product …' because the schema can't yet declare fallback semantics. Add to cart is a string label, not a `button`: it performs an action, it doesn't navigate. The gallery is optional and only overrides the store photos when set.",
|
|
1457
|
+
sanity: "object `productHero` { product: reference<product> (Sanity Connect for Shopify syncs product docs), headline: string, tagline, badge, highlights: array<object>, pickers: array<string>, price: string, compareAt: boolean, addLabel: string, gallery: array<image> } — Sanity also can't infer the picker list from the referenced product.",
|
|
1458
|
+
htmlSignals: [
|
|
1459
|
+
"A product form: <form action=\"/cart/add\"> with <select name=\"id\"> or radio swatches and a submit button",
|
|
1460
|
+
"Price markup with a <s>/<del> compare-at price and currency symbol; Klarna/Afterpay/Shop Pay installment widgets",
|
|
1461
|
+
"Product JSON (<script type=\"application/json\" data-product-json> or ShopifyAnalytics.meta.product)",
|
|
1462
|
+
"A thumbnail strip + main image (gallery) beside the buy box",
|
|
1463
|
+
"JSON-LD @type Product with offers (native — derived from the reference)"
|
|
1464
|
+
],
|
|
1465
|
+
mcp: "Product form at the top of a landing/product page → `productHero`. Resolve the product by its handle/id from the product JSON → product reference. NEVER copy price, compare-at, SKU, stock or option values into fields. If the visible H1 differs from the product title → heading override; else leave empty. Option groups shown as pickers → pickers (by option name: Color/Color → color). Submit button text → addLabel. Gallery images that equal the store's product media → leave gallery empty.",
|
|
1466
|
+
pitfalls: ["Hard-coding the price in the tagline ('Just $165!') — it goes stale; flag any currency in copy.", "Picker options are a fixed list here; a product with a 'Handle wrap' option can't be picked until options come from the reference (gap)."]
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
id: "hero-search",
|
|
1471
|
+
previewLayout: "centered",
|
|
1472
|
+
label: "Search hero",
|
|
1473
|
+
proves: "Search box config · manual vs automatic suggestions · topic chips as references · scope and results page",
|
|
1474
|
+
category: "Heroes",
|
|
1475
|
+
tags: [
|
|
1476
|
+
"references",
|
|
1477
|
+
"conditional",
|
|
1478
|
+
"dynamic-query",
|
|
1479
|
+
"media-heavy"
|
|
1480
|
+
],
|
|
1481
|
+
brand: "Caldera",
|
|
1482
|
+
schema: {
|
|
1483
|
+
name: "searchHero",
|
|
1484
|
+
title: "Search hero",
|
|
1485
|
+
kind: "block",
|
|
1486
|
+
icon: "input-search",
|
|
1487
|
+
category: "Heroes",
|
|
1488
|
+
description: "A headline with a big search box, suggested searches and topic chips.",
|
|
1489
|
+
preview: {
|
|
1490
|
+
title: "heading",
|
|
1491
|
+
subtitle: "placeholder",
|
|
1492
|
+
media: "backgroundImage"
|
|
1493
|
+
},
|
|
1494
|
+
groups: [
|
|
1495
|
+
{
|
|
1496
|
+
name: "content",
|
|
1497
|
+
title: "Content",
|
|
1498
|
+
default: true
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
name: "search",
|
|
1502
|
+
title: "Search"
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
name: "media",
|
|
1506
|
+
title: "Media"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
name: "design",
|
|
1510
|
+
title: "Design"
|
|
1511
|
+
}
|
|
1512
|
+
],
|
|
1513
|
+
fields: [
|
|
1514
|
+
eyebrow,
|
|
1515
|
+
heading(48),
|
|
1516
|
+
{
|
|
1517
|
+
name: "subheading",
|
|
1518
|
+
title: "Subheading",
|
|
1519
|
+
type: "text",
|
|
1520
|
+
rows: 2,
|
|
1521
|
+
validation: { recommendedMax: 120 },
|
|
1522
|
+
localized: true
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: "placeholder",
|
|
1526
|
+
title: "Placeholder",
|
|
1527
|
+
type: "string",
|
|
1528
|
+
group: "search",
|
|
1529
|
+
validation: {
|
|
1530
|
+
required: true,
|
|
1531
|
+
max: 60
|
|
1532
|
+
},
|
|
1533
|
+
localized: true,
|
|
1534
|
+
placeholder: "Try dutch oven bread"
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
name: "buttonLabel",
|
|
1538
|
+
title: "Search button label",
|
|
1539
|
+
type: "string",
|
|
1540
|
+
group: "search",
|
|
1541
|
+
initialValue: "Search",
|
|
1542
|
+
validation: { max: 16 },
|
|
1543
|
+
description: "Read by screen readers when the button shows only an icon."
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
name: "scope",
|
|
1547
|
+
title: "Searches",
|
|
1548
|
+
type: "select",
|
|
1549
|
+
group: "search",
|
|
1550
|
+
layout: "radio",
|
|
1551
|
+
initialValue: "recipes",
|
|
1552
|
+
options: [
|
|
1553
|
+
{
|
|
1554
|
+
value: "recipes",
|
|
1555
|
+
label: "Recipes"
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
value: "journal",
|
|
1559
|
+
label: "Journal stories"
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
value: "products",
|
|
1563
|
+
label: "Products"
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
value: "all",
|
|
1567
|
+
label: "The whole site"
|
|
1568
|
+
}
|
|
1569
|
+
]
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
name: "suggestionSource",
|
|
1573
|
+
title: "Suggested searches",
|
|
1574
|
+
type: "select",
|
|
1575
|
+
group: "search",
|
|
1576
|
+
layout: "segmented",
|
|
1577
|
+
initialValue: "manual",
|
|
1578
|
+
options: [
|
|
1579
|
+
{
|
|
1580
|
+
value: "manual",
|
|
1581
|
+
label: "Written here"
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
value: "popular",
|
|
1585
|
+
label: "Most searched"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
value: "none",
|
|
1589
|
+
label: "None"
|
|
1590
|
+
}
|
|
1591
|
+
]
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
name: "suggestions",
|
|
1595
|
+
title: "Suggestions",
|
|
1596
|
+
type: "array",
|
|
1597
|
+
itemNoun: "suggestion",
|
|
1598
|
+
of: [{
|
|
1599
|
+
name: "suggestion",
|
|
1600
|
+
title: "Suggestion",
|
|
1601
|
+
type: "string"
|
|
1602
|
+
}],
|
|
1603
|
+
group: "search",
|
|
1604
|
+
hidden: {
|
|
1605
|
+
field: "suggestionSource",
|
|
1606
|
+
equals: "manual"
|
|
1607
|
+
},
|
|
1608
|
+
validation: { max: 6 }
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
name: "popularDays",
|
|
1612
|
+
title: "Count searches from the last",
|
|
1613
|
+
type: "number",
|
|
1614
|
+
group: "search",
|
|
1615
|
+
hidden: {
|
|
1616
|
+
field: "suggestionSource",
|
|
1617
|
+
equals: "popular"
|
|
1618
|
+
},
|
|
1619
|
+
numeric: {
|
|
1620
|
+
min: 7,
|
|
1621
|
+
max: 90,
|
|
1622
|
+
step: 1,
|
|
1623
|
+
unit: "days"
|
|
1624
|
+
},
|
|
1625
|
+
initialValue: 30
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
name: "topics",
|
|
1629
|
+
title: "Topic chips",
|
|
1630
|
+
type: "array",
|
|
1631
|
+
itemNoun: "topic",
|
|
1632
|
+
validation: { max: 6 },
|
|
1633
|
+
of: [{
|
|
1634
|
+
name: "topic",
|
|
1635
|
+
title: "Topic",
|
|
1636
|
+
type: "reference",
|
|
1637
|
+
reference: { to: ["col_topics"] }
|
|
1638
|
+
}]
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
name: "backgroundImage",
|
|
1642
|
+
title: "Background image",
|
|
1643
|
+
type: "image",
|
|
1644
|
+
group: "media"
|
|
1645
|
+
},
|
|
1646
|
+
overlay,
|
|
1647
|
+
{
|
|
1648
|
+
...align,
|
|
1649
|
+
initialValue: "center"
|
|
1650
|
+
},
|
|
1651
|
+
theme
|
|
1652
|
+
]
|
|
1653
|
+
},
|
|
1654
|
+
value: {
|
|
1655
|
+
eyebrow: "400+ tested recipes",
|
|
1656
|
+
heading: "What are you cooking tonight?",
|
|
1657
|
+
subheading: "Every recipe is tested over coals and on a stovetop, with notes for both.",
|
|
1658
|
+
placeholder: "Try “dutch oven bread”",
|
|
1659
|
+
buttonLabel: "Search",
|
|
1660
|
+
scope: "recipes",
|
|
1661
|
+
suggestionSource: "manual",
|
|
1662
|
+
suggestions: [
|
|
1663
|
+
"dutch oven bread",
|
|
1664
|
+
"skillet cornbread",
|
|
1665
|
+
"one-pot chili",
|
|
1666
|
+
"campfire breakfast"
|
|
1667
|
+
],
|
|
1668
|
+
popularDays: 30,
|
|
1669
|
+
topics: [
|
|
1670
|
+
"tp_camp",
|
|
1671
|
+
"tp_recipes",
|
|
1672
|
+
"tp_cast-iron",
|
|
1673
|
+
"tp_care"
|
|
1674
|
+
],
|
|
1675
|
+
backgroundImage: pic("caldera-recipes-hero", "Overhead view of a camp table with chili, cornbread and a cast iron pan", 2400, 1200),
|
|
1676
|
+
overlay: 50,
|
|
1677
|
+
alignment: "center",
|
|
1678
|
+
theme: "dark"
|
|
1679
|
+
},
|
|
1680
|
+
notes: {
|
|
1681
|
+
why: "The search box itself is behavior, so the block stores its CONFIG: what it searches (scope), what it says (placeholder, an accessible button label) and where suggestions come from. `suggestionSource` switches between hand-written tags and a dynamic 'most searched' query (with its window), so a converter can mark a site's trending list as dynamic instead of freezing it. Topic chips are references to the Topics collection, so a renamed topic updates here. Results render on the site's search page — no results UI is modelled.",
|
|
1682
|
+
sanity: "object `searchHero` { heading, placeholder, scope: string, suggestionSource: string, suggestions: array<string>, topics: array<reference<topic>>, image } — the 'popular' mode would be a GROQ/Algolia query in the frontend, not data.",
|
|
1683
|
+
htmlSignals: [
|
|
1684
|
+
"A hero whose main control is <form role=\"search\"> / <input type=\"search\"> with a large placeholder",
|
|
1685
|
+
"Links below the input like /search?q=… ('Popular: …') — suggestions",
|
|
1686
|
+
"Pill links to /topics/*, /tags/* or /category/* — topic chips",
|
|
1687
|
+
"Form action pointing to /search, /recipes or an Algolia/Typesense endpoint (scope)"
|
|
1688
|
+
],
|
|
1689
|
+
mcp: "Hero containing a search form → `searchHero`. placeholder attribute → placeholder; the form action path → scope (/recipes → 'recipes', /search → 'all'). Links with ?q= → suggestions (the q values, decoded) and suggestionSource 'manual'; if they change between two crawls a day apart → 'popular'. Pills linking to taxonomy pages → topics, resolved to Topics entries by slug (create missing ones and report).",
|
|
1690
|
+
pitfalls: ["Don't model the autocomplete dropdown's results — they come from the search index."]
|
|
1691
|
+
}
|
|
1692
|
+
},
|
|
1693
|
+
{
|
|
1694
|
+
id: "hero-lead-form",
|
|
1695
|
+
previewLayout: "form",
|
|
1696
|
+
label: "Lead-form hero",
|
|
1697
|
+
proves: "A form REFERENCE plus per-placement settings: hidden source fields, label override, message vs redirect",
|
|
1698
|
+
category: "Heroes",
|
|
1699
|
+
tags: [
|
|
1700
|
+
"form",
|
|
1701
|
+
"references",
|
|
1702
|
+
"conditional",
|
|
1703
|
+
"validation",
|
|
1704
|
+
"repeater"
|
|
1705
|
+
],
|
|
1706
|
+
brand: "Northbound Studio",
|
|
1707
|
+
schema: {
|
|
1708
|
+
name: "leadFormHero",
|
|
1709
|
+
title: "Lead form hero",
|
|
1710
|
+
kind: "block",
|
|
1711
|
+
icon: "clipboard-text",
|
|
1712
|
+
category: "Heroes",
|
|
1713
|
+
description: "A pitch on one side and a short form on the other.",
|
|
1714
|
+
variants: [
|
|
1715
|
+
{
|
|
1716
|
+
id: "form-right",
|
|
1717
|
+
name: "Form right"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
id: "form-left",
|
|
1721
|
+
name: "Form left"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
id: "form-below",
|
|
1725
|
+
name: "Form below",
|
|
1726
|
+
description: "Centered pitch with the form underneath"
|
|
1727
|
+
}
|
|
1728
|
+
],
|
|
1729
|
+
preview: {
|
|
1730
|
+
title: "heading",
|
|
1731
|
+
subtitle: "formHeading",
|
|
1732
|
+
media: "image"
|
|
1733
|
+
},
|
|
1734
|
+
groups: [
|
|
1735
|
+
{
|
|
1736
|
+
name: "content",
|
|
1737
|
+
title: "Pitch",
|
|
1738
|
+
default: true
|
|
1739
|
+
},
|
|
1740
|
+
{
|
|
1741
|
+
name: "form",
|
|
1742
|
+
title: "Form"
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
name: "design",
|
|
1746
|
+
title: "Design"
|
|
1747
|
+
}
|
|
1748
|
+
],
|
|
1749
|
+
fields: [
|
|
1750
|
+
eyebrow,
|
|
1751
|
+
heading(64),
|
|
1752
|
+
body(200),
|
|
1753
|
+
{
|
|
1754
|
+
name: "points",
|
|
1755
|
+
title: "What they get",
|
|
1756
|
+
type: "array",
|
|
1757
|
+
itemNoun: "point",
|
|
1758
|
+
validation: { max: 4 },
|
|
1759
|
+
of: [{
|
|
1760
|
+
name: "point",
|
|
1761
|
+
title: "Point",
|
|
1762
|
+
type: "string",
|
|
1763
|
+
validation: { max: 60 }
|
|
1764
|
+
}]
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
name: "trustLine",
|
|
1768
|
+
title: "Trust line",
|
|
1769
|
+
type: "string",
|
|
1770
|
+
validation: { max: 90 }
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
name: "image",
|
|
1774
|
+
title: "Image",
|
|
1775
|
+
type: "image"
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
name: "form",
|
|
1779
|
+
title: "Form",
|
|
1780
|
+
type: "reference",
|
|
1781
|
+
group: "form",
|
|
1782
|
+
reference: { to: ["form"] },
|
|
1783
|
+
validation: { required: true }
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
name: "formHeading",
|
|
1787
|
+
title: "Form heading",
|
|
1788
|
+
type: "string",
|
|
1789
|
+
group: "form",
|
|
1790
|
+
validation: { max: 48 }
|
|
1791
|
+
},
|
|
1792
|
+
{
|
|
1793
|
+
name: "submitLabel",
|
|
1794
|
+
title: "Submit label",
|
|
1795
|
+
type: "string",
|
|
1796
|
+
group: "form",
|
|
1797
|
+
validation: { max: 24 },
|
|
1798
|
+
description: "Leave empty to use the label set on the form."
|
|
1799
|
+
},
|
|
1800
|
+
{
|
|
1801
|
+
name: "afterSubmit",
|
|
1802
|
+
title: "After sending",
|
|
1803
|
+
type: "select",
|
|
1804
|
+
group: "form",
|
|
1805
|
+
layout: "radio",
|
|
1806
|
+
initialValue: "message",
|
|
1807
|
+
options: [{
|
|
1808
|
+
value: "message",
|
|
1809
|
+
label: "Show a message",
|
|
1810
|
+
description: "The form is replaced by your thank-you note"
|
|
1811
|
+
}, {
|
|
1812
|
+
value: "redirect",
|
|
1813
|
+
label: "Go to a page",
|
|
1814
|
+
description: "Best when you track conversions on a thank-you page"
|
|
1815
|
+
}]
|
|
1816
|
+
},
|
|
1817
|
+
{
|
|
1818
|
+
name: "successMessage",
|
|
1819
|
+
title: "Thank-you message",
|
|
1820
|
+
type: "text",
|
|
1821
|
+
group: "form",
|
|
1822
|
+
rows: 2,
|
|
1823
|
+
hidden: {
|
|
1824
|
+
field: "afterSubmit",
|
|
1825
|
+
equals: "message"
|
|
1826
|
+
},
|
|
1827
|
+
validation: {
|
|
1828
|
+
required: true,
|
|
1829
|
+
max: 240
|
|
1830
|
+
}
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
name: "redirect",
|
|
1834
|
+
title: "Thank-you page",
|
|
1835
|
+
type: "link",
|
|
1836
|
+
group: "form",
|
|
1837
|
+
hidden: {
|
|
1838
|
+
field: "afterSubmit",
|
|
1839
|
+
equals: "redirect"
|
|
1840
|
+
},
|
|
1841
|
+
validation: { required: true }
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
name: "hiddenFields",
|
|
1845
|
+
title: "Hidden values",
|
|
1846
|
+
type: "array",
|
|
1847
|
+
group: "form",
|
|
1848
|
+
itemNoun: "value",
|
|
1849
|
+
validation: { max: 6 },
|
|
1850
|
+
description: "Not shown. Sent with every submission from this section.",
|
|
1851
|
+
of: [{
|
|
1852
|
+
name: "hiddenField",
|
|
1853
|
+
title: "Hidden value",
|
|
1854
|
+
type: "object",
|
|
1855
|
+
icon: "eye-off",
|
|
1856
|
+
openMode: "inline",
|
|
1857
|
+
preview: {
|
|
1858
|
+
title: "key",
|
|
1859
|
+
subtitle: "value",
|
|
1860
|
+
fallback: "Untitled value"
|
|
1861
|
+
},
|
|
1862
|
+
fields: [{
|
|
1863
|
+
name: "key",
|
|
1864
|
+
title: "Key",
|
|
1865
|
+
type: "string",
|
|
1866
|
+
validation: {
|
|
1867
|
+
required: true,
|
|
1868
|
+
unique: true,
|
|
1869
|
+
pattern: "^[a-z][a-z0-9_]*$",
|
|
1870
|
+
patternMessage: "Use lowercase letters, numbers and underscores, like page_view."
|
|
1871
|
+
},
|
|
1872
|
+
placeholder: "source"
|
|
1873
|
+
}, {
|
|
1874
|
+
name: "value",
|
|
1875
|
+
title: "Value",
|
|
1876
|
+
type: "string",
|
|
1877
|
+
validation: {
|
|
1878
|
+
required: true,
|
|
1879
|
+
max: 80
|
|
1880
|
+
},
|
|
1881
|
+
placeholder: "homepage-hero"
|
|
1882
|
+
}]
|
|
1883
|
+
}]
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "privacyNote",
|
|
1887
|
+
title: "Privacy note",
|
|
1888
|
+
type: "string",
|
|
1889
|
+
group: "form",
|
|
1890
|
+
validation: { max: 120 }
|
|
1891
|
+
},
|
|
1892
|
+
theme
|
|
1893
|
+
]
|
|
1894
|
+
},
|
|
1895
|
+
value: {
|
|
1896
|
+
_variant: "form-right",
|
|
1897
|
+
eyebrow: "Brand & web for outdoor companies",
|
|
1898
|
+
heading: "Your next site should sell as hard as your product works.",
|
|
1899
|
+
body: "We design and build brand-led commerce sites for outdoor and food companies — strategy, identity and a CMS your team actually enjoys.",
|
|
1900
|
+
points: [
|
|
1901
|
+
"A 30-minute call with a founder, not a salesperson",
|
|
1902
|
+
"A written plan within three working days",
|
|
1903
|
+
"Fixed quotes, no hourly surprises"
|
|
1904
|
+
],
|
|
1905
|
+
trustLine: "Trusted by teams at Caldera, Tideline and Open Water Trust.",
|
|
1906
|
+
image: pic("northbound-studio-desk", "Two designers reviewing printed brand boards on a studio wall", 1600, 1200),
|
|
1907
|
+
form: "form_contact",
|
|
1908
|
+
formHeading: "Book a strategy call",
|
|
1909
|
+
submitLabel: "Request a call",
|
|
1910
|
+
afterSubmit: "redirect",
|
|
1911
|
+
successMessage: "Thanks — we'll reply within one working day with a few times to talk.",
|
|
1912
|
+
redirect: {
|
|
1913
|
+
type: "page",
|
|
1914
|
+
href: "/thanks",
|
|
1915
|
+
title: "Thanks"
|
|
1916
|
+
},
|
|
1917
|
+
hiddenFields: [{
|
|
1918
|
+
_key: "k1",
|
|
1919
|
+
key: "source",
|
|
1920
|
+
value: "homepage-hero"
|
|
1921
|
+
}, {
|
|
1922
|
+
_key: "k2",
|
|
1923
|
+
key: "service",
|
|
1924
|
+
value: "brand-and-web"
|
|
1925
|
+
}],
|
|
1926
|
+
privacyNote: "We'll only use your details to reply. No newsletters unless you ask.",
|
|
1927
|
+
theme: "light"
|
|
1928
|
+
},
|
|
1929
|
+
notes: {
|
|
1930
|
+
why: "The form is a separate form in Forms referenced by the hero — its inputs, validation, spam protection and where submissions go belong to Forms and are reused across the site. What's specific to THIS placement stays here: a heading, a submit-label override, what happens after sending (message vs redirect, with the right field shown for each) and hidden values that tag where leads came from. Modelling the inputs as hero fields would fork the form every time it's placed.",
|
|
1931
|
+
sanity: "object `leadFormHero` { heading, body, points: array<string>, form: reference<form> (or an external Hubspot/Formspark id string), submitLabel, afterSubmit: string, successMessage: text (hidden unless message), redirect: link, hiddenFields: array<object { key, value }> }.",
|
|
1932
|
+
htmlSignals: [
|
|
1933
|
+
"A hero section that contains a <form> with 3–6 inputs (name, email, company, message) beside or below the pitch copy",
|
|
1934
|
+
"<input type=\"hidden\" name=\"source\" value=\"…\"> or utm_* hidden inputs",
|
|
1935
|
+
"HubSpot/Typeform/Formspark embeds (hbspt.forms.create, data-tf-widget) — a third-party form",
|
|
1936
|
+
"A small privacy sentence under the submit button"
|
|
1937
|
+
],
|
|
1938
|
+
mcp: "Hero + <form> → `leadFormHero`. Create (or match by its input set) a BetterCMS form from the inputs — labels, types, required — and set `form` to its id. Hidden inputs with static values → hiddenFields (skip ones filled by script, e.g. utm_* read from the URL — those are automatic). Submit text → submitLabel only if it differs from the form's default. A JS redirect after submit or form action to /thanks → afterSubmit 'redirect'. For HubSpot/Typeform embeds, report the provider and form id instead of recreating it.",
|
|
1939
|
+
pitfalls: ["Copying inputs into the hero's schema — the form then can't be reused or receive submissions properly.", "Hidden values that contain personal data (an email from the URL) must not be stored in content."]
|
|
1940
|
+
}
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
id: "hero-app-download",
|
|
1944
|
+
previewLayout: "split",
|
|
1945
|
+
label: "App download hero",
|
|
1946
|
+
proves: "A field synced from a global (store links) inside a normal block · QR code derived from a link · device screens",
|
|
1947
|
+
category: "Heroes",
|
|
1948
|
+
tags: [
|
|
1949
|
+
"media-heavy",
|
|
1950
|
+
"conditional",
|
|
1951
|
+
"validation",
|
|
1952
|
+
"global-synced",
|
|
1953
|
+
"repeater"
|
|
1954
|
+
],
|
|
1955
|
+
brand: "Hearth & Ash",
|
|
1956
|
+
schema: {
|
|
1957
|
+
name: "appDownloadHero",
|
|
1958
|
+
title: "App download hero",
|
|
1959
|
+
kind: "block",
|
|
1960
|
+
icon: "device-mobile",
|
|
1961
|
+
category: "Heroes",
|
|
1962
|
+
description: "Pitch the app with store badges, a QR code for desktop visitors and phone screenshots.",
|
|
1963
|
+
preview: {
|
|
1964
|
+
title: "heading",
|
|
1965
|
+
subtitle: "eyebrow",
|
|
1966
|
+
media: "screens.0"
|
|
1967
|
+
},
|
|
1968
|
+
groups: GROUPS,
|
|
1969
|
+
fields: [
|
|
1970
|
+
eyebrow,
|
|
1971
|
+
heading(56),
|
|
1972
|
+
body(160),
|
|
1973
|
+
{
|
|
1974
|
+
name: "badges",
|
|
1975
|
+
title: "Store links",
|
|
1976
|
+
type: "array",
|
|
1977
|
+
itemNoun: "store",
|
|
1978
|
+
synced: { from: "App links" },
|
|
1979
|
+
validation: {
|
|
1980
|
+
min: 1,
|
|
1981
|
+
max: 2
|
|
1982
|
+
},
|
|
1983
|
+
description: "Shared: changes apply to every badge on the site.",
|
|
1984
|
+
of: [{
|
|
1985
|
+
name: "storeLink",
|
|
1986
|
+
title: "Store",
|
|
1987
|
+
type: "object",
|
|
1988
|
+
icon: "device-mobile",
|
|
1989
|
+
openMode: "inline",
|
|
1990
|
+
preview: {
|
|
1991
|
+
title: "store",
|
|
1992
|
+
subtitle: "url"
|
|
1993
|
+
},
|
|
1994
|
+
fields: [{
|
|
1995
|
+
name: "store",
|
|
1996
|
+
title: "Store",
|
|
1997
|
+
type: "select",
|
|
1998
|
+
validation: {
|
|
1999
|
+
required: true,
|
|
2000
|
+
unique: true
|
|
2001
|
+
},
|
|
2002
|
+
options: [{
|
|
2003
|
+
value: "app-store",
|
|
2004
|
+
label: "App Store"
|
|
2005
|
+
}, {
|
|
2006
|
+
value: "google-play",
|
|
2007
|
+
label: "Google Play"
|
|
2008
|
+
}]
|
|
2009
|
+
}, {
|
|
2010
|
+
name: "url",
|
|
2011
|
+
title: "Store address",
|
|
2012
|
+
type: "url",
|
|
2013
|
+
validation: {
|
|
2014
|
+
required: true,
|
|
2015
|
+
pattern: "^https://(apps\\.apple\\.com|play\\.google\\.com)/",
|
|
2016
|
+
patternMessage: "Use an apps.apple.com or play.google.com address, like https://apps.apple.com/app/id123."
|
|
2017
|
+
}
|
|
2018
|
+
}]
|
|
2019
|
+
}]
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
name: "qr",
|
|
2023
|
+
title: "QR code",
|
|
2024
|
+
type: "object",
|
|
2025
|
+
icon: "qrcode",
|
|
2026
|
+
openMode: "drill",
|
|
2027
|
+
fields: [
|
|
2028
|
+
{
|
|
2029
|
+
name: "showQrCode",
|
|
2030
|
+
title: "Show a QR code",
|
|
2031
|
+
type: "boolean",
|
|
2032
|
+
initialValue: true,
|
|
2033
|
+
description: "Desktop only. Phones see the badges."
|
|
2034
|
+
},
|
|
2035
|
+
{
|
|
2036
|
+
name: "target",
|
|
2037
|
+
title: "QR opens",
|
|
2038
|
+
type: "url",
|
|
2039
|
+
hidden: {
|
|
2040
|
+
field: "showQrCode",
|
|
2041
|
+
equals: true
|
|
2042
|
+
},
|
|
2043
|
+
validation: { required: true },
|
|
2044
|
+
description: "One link that sends iPhones and Androids to the right store."
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
name: "caption",
|
|
2048
|
+
title: "Caption",
|
|
2049
|
+
type: "string",
|
|
2050
|
+
hidden: {
|
|
2051
|
+
field: "showQrCode",
|
|
2052
|
+
equals: true
|
|
2053
|
+
},
|
|
2054
|
+
validation: { max: 32 },
|
|
2055
|
+
placeholder: "Scan to download"
|
|
2056
|
+
}
|
|
2057
|
+
]
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
name: "rating",
|
|
2061
|
+
title: "Store rating",
|
|
2062
|
+
type: "object",
|
|
2063
|
+
icon: "star",
|
|
2064
|
+
fields: [{
|
|
2065
|
+
name: "value",
|
|
2066
|
+
title: "Rating",
|
|
2067
|
+
type: "number",
|
|
2068
|
+
numeric: {
|
|
2069
|
+
min: 0,
|
|
2070
|
+
max: 5,
|
|
2071
|
+
step: .1
|
|
2072
|
+
}
|
|
2073
|
+
}, {
|
|
2074
|
+
name: "label",
|
|
2075
|
+
title: "Label",
|
|
2076
|
+
type: "string",
|
|
2077
|
+
validation: { max: 32 },
|
|
2078
|
+
placeholder: "12,400 ratings"
|
|
2079
|
+
}]
|
|
2080
|
+
},
|
|
2081
|
+
{
|
|
2082
|
+
name: "screens",
|
|
2083
|
+
title: "Screenshots",
|
|
2084
|
+
type: "array",
|
|
2085
|
+
group: "media",
|
|
2086
|
+
arrayLayout: "grid",
|
|
2087
|
+
itemNoun: "screenshot",
|
|
2088
|
+
validation: {
|
|
2089
|
+
min: 1,
|
|
2090
|
+
max: 3
|
|
2091
|
+
},
|
|
2092
|
+
description: "Phone screens at 1170 × 2532.",
|
|
2093
|
+
of: [{
|
|
2094
|
+
name: "screen",
|
|
2095
|
+
title: "Screenshot",
|
|
2096
|
+
type: "image"
|
|
2097
|
+
}]
|
|
2098
|
+
},
|
|
2099
|
+
{
|
|
2100
|
+
name: "device",
|
|
2101
|
+
title: "Device frame",
|
|
2102
|
+
type: "select",
|
|
2103
|
+
group: "design",
|
|
2104
|
+
layout: "segmented",
|
|
2105
|
+
initialValue: "iphone",
|
|
2106
|
+
options: [
|
|
2107
|
+
{
|
|
2108
|
+
value: "iphone",
|
|
2109
|
+
label: "iPhone"
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
value: "android",
|
|
2113
|
+
label: "Android"
|
|
2114
|
+
},
|
|
2115
|
+
{
|
|
2116
|
+
value: "none",
|
|
2117
|
+
label: "No frame"
|
|
2118
|
+
}
|
|
2119
|
+
]
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
name: "background",
|
|
2123
|
+
title: "Background color",
|
|
2124
|
+
type: "color",
|
|
2125
|
+
group: "design"
|
|
2126
|
+
},
|
|
2127
|
+
theme
|
|
2128
|
+
]
|
|
2129
|
+
},
|
|
2130
|
+
value: {
|
|
2131
|
+
eyebrow: "The Hearth & Ash app",
|
|
2132
|
+
heading: "Skip the line. Your table is waiting.",
|
|
2133
|
+
body: "Book in two taps, order from the bar while you wait, and pay at the table without hunting down the check.",
|
|
2134
|
+
badges: [{
|
|
2135
|
+
_key: "b1",
|
|
2136
|
+
store: "app-store",
|
|
2137
|
+
url: "https://apps.apple.com/us/app/hearth-ash/id6450012345"
|
|
2138
|
+
}, {
|
|
2139
|
+
_key: "b2",
|
|
2140
|
+
store: "google-play",
|
|
2141
|
+
url: "https://play.google.com/store/apps/details?id=com.hearthandash.order"
|
|
2142
|
+
}],
|
|
2143
|
+
qr: {
|
|
2144
|
+
showQrCode: true,
|
|
2145
|
+
target: "https://hearthandash.com/app",
|
|
2146
|
+
caption: "Scan to download"
|
|
2147
|
+
},
|
|
2148
|
+
rating: {
|
|
2149
|
+
value: 4.8,
|
|
2150
|
+
label: "2,140 ratings"
|
|
2151
|
+
},
|
|
2152
|
+
screens: [pic("hearth-app-book", "App screen: choose a time for a table for four at Pearl District", 1170, 2532), pic("hearth-app-order", "App screen: ordering two cocktails from the bar menu", 1170, 2532)],
|
|
2153
|
+
device: "iphone",
|
|
2154
|
+
background: "#2A1A14",
|
|
2155
|
+
theme: "dark"
|
|
2156
|
+
},
|
|
2157
|
+
notes: {
|
|
2158
|
+
why: "Store links appear in the hero, the footer and emails, so the badges field is SYNCED from an 'App links' global (field-level `synced`) — one App Store URL change updates every badge, while the rest of the hero stays local. The QR code is DERIVED from a URL (we store the target, never an uploaded QR image that can't be updated); its fields only show when it's on. Badges are a select + URL with a pattern because the renderer draws the official artwork. Screenshots are a small image array; the device frame and background are Design.",
|
|
2159
|
+
sanity: "object `appHero` { badges: reference<appLinks> (a singleton), qr: object { show, target: url, caption }, rating: object { value, label }, screens: array<image>, device: string }. Sanity has no field-level sync; it's a reference to a singleton.",
|
|
2160
|
+
htmlSignals: [
|
|
2161
|
+
"Official App Store / Google Play badge images linking to apps.apple.com and play.google.com",
|
|
2162
|
+
"A QR code <img>/<svg>/<canvas> near the badges, often desktop-only",
|
|
2163
|
+
"Phone mockup frames (rounded rect with notch) around screenshots, sometimes overlapping",
|
|
2164
|
+
"Smart-banner or apple-itunes-app meta tag in <head> (an app exists)"
|
|
2165
|
+
],
|
|
2166
|
+
mcp: "Hero with store badges → `appDownloadHero`. Badge hrefs → badges[] { store by hostname, url }; also create/update the 'App links' global with them. A QR image: decode it if possible and set qr.target to the decoded URL (never store the QR image); else ask. Screenshots inside device frames → screens (crop to the screen area if the frame is baked into the image, and report). Star rating text → rating.",
|
|
2167
|
+
pitfalls: ["Uploading badge or QR images — they can't be updated and the QR silently breaks when the link changes.", "Ratings copied from the store go stale; the help text says so because there's no live source."]
|
|
2168
|
+
}
|
|
2169
|
+
},
|
|
2170
|
+
{
|
|
2171
|
+
id: "hero-stats",
|
|
2172
|
+
previewLayout: "stats",
|
|
2173
|
+
label: "Stats hero",
|
|
2174
|
+
proves: "Numbers stored as numbers with prefix/suffix (count-up safe) · footnoted sources · an 'as of' date",
|
|
2175
|
+
category: "Heroes",
|
|
2176
|
+
tags: [
|
|
2177
|
+
"repeater",
|
|
2178
|
+
"validation",
|
|
2179
|
+
"variants",
|
|
2180
|
+
"media-heavy"
|
|
2181
|
+
],
|
|
2182
|
+
brand: "Open Water Trust",
|
|
2183
|
+
schema: {
|
|
2184
|
+
name: "statsHero",
|
|
2185
|
+
title: "Stats hero",
|
|
2186
|
+
kind: "block",
|
|
2187
|
+
icon: "chart-arrows",
|
|
2188
|
+
category: "Heroes",
|
|
2189
|
+
description: "A headline backed by a row of two to four numbers.",
|
|
2190
|
+
variants: [{
|
|
2191
|
+
id: "below",
|
|
2192
|
+
name: "Stats below",
|
|
2193
|
+
description: "A row of numbers under the buttons"
|
|
2194
|
+
}, {
|
|
2195
|
+
id: "side",
|
|
2196
|
+
name: "Stats beside",
|
|
2197
|
+
description: "Copy left, a stacked column of numbers right"
|
|
2198
|
+
}],
|
|
2199
|
+
preview: {
|
|
2200
|
+
title: "heading",
|
|
2201
|
+
subtitle: "eyebrow",
|
|
2202
|
+
media: "image"
|
|
2203
|
+
},
|
|
2204
|
+
groups: GROUPS,
|
|
2205
|
+
fields: [
|
|
2206
|
+
eyebrow,
|
|
2207
|
+
heading(64),
|
|
2208
|
+
body(180),
|
|
2209
|
+
primary,
|
|
2210
|
+
secondary,
|
|
2211
|
+
{
|
|
2212
|
+
name: "stats",
|
|
2213
|
+
title: "Stats",
|
|
2214
|
+
type: "array",
|
|
2215
|
+
itemNoun: "stat",
|
|
2216
|
+
validation: {
|
|
2217
|
+
min: 2,
|
|
2218
|
+
max: 4
|
|
2219
|
+
},
|
|
2220
|
+
of: [{
|
|
2221
|
+
name: "stat",
|
|
2222
|
+
title: "Stat",
|
|
2223
|
+
type: "object",
|
|
2224
|
+
icon: "chart-bar",
|
|
2225
|
+
preview: {
|
|
2226
|
+
title: "label",
|
|
2227
|
+
subtitle: "value",
|
|
2228
|
+
fallback: "Untitled stat"
|
|
2229
|
+
},
|
|
2230
|
+
fields: [
|
|
2231
|
+
{
|
|
2232
|
+
name: "prefix",
|
|
2233
|
+
title: "Before",
|
|
2234
|
+
type: "string",
|
|
2235
|
+
fieldset: "Number",
|
|
2236
|
+
validation: { max: 3 },
|
|
2237
|
+
placeholder: "$"
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
name: "value",
|
|
2241
|
+
title: "Number",
|
|
2242
|
+
type: "number",
|
|
2243
|
+
fieldset: "Number",
|
|
2244
|
+
validation: { required: true },
|
|
2245
|
+
numeric: {
|
|
2246
|
+
min: 0,
|
|
2247
|
+
step: .1
|
|
2248
|
+
}
|
|
2249
|
+
},
|
|
2250
|
+
{
|
|
2251
|
+
name: "suffix",
|
|
2252
|
+
title: "After",
|
|
2253
|
+
type: "string",
|
|
2254
|
+
fieldset: "Number",
|
|
2255
|
+
validation: { max: 6 },
|
|
2256
|
+
placeholder: "M, %, +"
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
name: "decimals",
|
|
2260
|
+
title: "Decimals",
|
|
2261
|
+
type: "number",
|
|
2262
|
+
fieldset: "Number",
|
|
2263
|
+
numeric: {
|
|
2264
|
+
min: 0,
|
|
2265
|
+
max: 2,
|
|
2266
|
+
step: 1
|
|
2267
|
+
},
|
|
2268
|
+
initialValue: 0
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
name: "label",
|
|
2272
|
+
title: "Label",
|
|
2273
|
+
type: "string",
|
|
2274
|
+
validation: {
|
|
2275
|
+
required: true,
|
|
2276
|
+
max: 40
|
|
2277
|
+
},
|
|
2278
|
+
placeholder: "river miles reopened"
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
name: "source",
|
|
2282
|
+
title: "Source",
|
|
2283
|
+
type: "string",
|
|
2284
|
+
validation: { max: 120 }
|
|
2285
|
+
}
|
|
2286
|
+
]
|
|
2287
|
+
}]
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
name: "asOf",
|
|
2291
|
+
title: "Figures as of",
|
|
2292
|
+
type: "date"
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
name: "image",
|
|
2296
|
+
title: "Image",
|
|
2297
|
+
type: "image",
|
|
2298
|
+
group: "media"
|
|
2299
|
+
},
|
|
2300
|
+
{
|
|
2301
|
+
name: "useCountUp",
|
|
2302
|
+
title: "Count up on scroll",
|
|
2303
|
+
type: "boolean",
|
|
2304
|
+
group: "design",
|
|
2305
|
+
initialValue: true
|
|
2306
|
+
},
|
|
2307
|
+
theme,
|
|
2308
|
+
spacing
|
|
2309
|
+
]
|
|
2310
|
+
},
|
|
2311
|
+
value: {
|
|
2312
|
+
_variant: "below",
|
|
2313
|
+
eyebrow: "2025 impact",
|
|
2314
|
+
heading: "Rivers recover fast when we get out of their way.",
|
|
2315
|
+
body: "Since 2009 we've removed dams, replanted banks and bought back water rights so rivers can run the way they used to.",
|
|
2316
|
+
primaryButton: btn("Give monthly", "/donate?frequency=monthly", "primary"),
|
|
2317
|
+
secondaryButton: btn("Read the 2025 report", "https://cdn.bettercms.site/owt/impact-report-2025.pdf", "outline", {
|
|
2318
|
+
type: "file",
|
|
2319
|
+
newTab: true
|
|
2320
|
+
}),
|
|
2321
|
+
stats: [
|
|
2322
|
+
{
|
|
2323
|
+
_key: "s1",
|
|
2324
|
+
value: 1240,
|
|
2325
|
+
suffix: "",
|
|
2326
|
+
decimals: 0,
|
|
2327
|
+
label: "river miles reopened",
|
|
2328
|
+
source: "OWT field surveys, 2009–2025"
|
|
2329
|
+
},
|
|
2330
|
+
{
|
|
2331
|
+
_key: "s2",
|
|
2332
|
+
value: 38,
|
|
2333
|
+
decimals: 0,
|
|
2334
|
+
label: "dams removed"
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
_key: "s3",
|
|
2338
|
+
prefix: "$",
|
|
2339
|
+
value: 4.2,
|
|
2340
|
+
suffix: "M",
|
|
2341
|
+
decimals: 1,
|
|
2342
|
+
label: "raised from 31,000 donors",
|
|
2343
|
+
source: "Audited accounts, FY2025"
|
|
2344
|
+
},
|
|
2345
|
+
{
|
|
2346
|
+
_key: "s4",
|
|
2347
|
+
value: 96,
|
|
2348
|
+
suffix: "%",
|
|
2349
|
+
decimals: 0,
|
|
2350
|
+
label: "of returning salmon runs monitored"
|
|
2351
|
+
}
|
|
2352
|
+
],
|
|
2353
|
+
asOf: "2026-03-31",
|
|
2354
|
+
image: pic("owt-river-restored", "A braided river winding through a green valley where a reservoir used to be", 2400, 1400),
|
|
2355
|
+
useCountUp: true,
|
|
2356
|
+
theme: "light",
|
|
2357
|
+
spacing: "spacious"
|
|
2358
|
+
},
|
|
2359
|
+
notes: {
|
|
2360
|
+
why: "Each stat stores the NUMBER as a number with separate prefix/suffix/decimals, not '$4.2M' as text — the renderer can count up, format per locale (4,2 M in German) and a validator can check it. Labels are short and required. `source` per stat plus an `asOf` date make claims auditable, which nonprofits and regulated industries need. Two layouts are variants; count-up is Design. The shared Stat block in the core library uses a string value — this entry exists to show when the number must stay a number.",
|
|
2361
|
+
sanity: "object `statsHero` { stats: array<object { prefix: string, value: number, suffix: string, decimals: number, label: string, source: string }> (min 2, max 4), asOf: date }.",
|
|
2362
|
+
htmlSignals: [
|
|
2363
|
+
"Large numerals (font-size ≥ 40px) in a row of 2–4 items, each with a short caption under it",
|
|
2364
|
+
"data-count / data-target attributes or a countUp.js/odometer script",
|
|
2365
|
+
"Footnote markers (¹ ² or *) next to numbers with sources at the bottom",
|
|
2366
|
+
"Text like 'as of March 2026' or 'FY2025' near the row"
|
|
2367
|
+
],
|
|
2368
|
+
mcp: "Hero + 2–4 big numerals → `statsHero`. Parse each numeral: leading non-digits → prefix, trailing letters/% /+ → suffix, the number → value (use data-target when present — the visible text may be mid-animation, e.g. '0'), decimals from the fraction part. Caption → label. Footnotes → source on the matching stat. A date near the row → asOf (ISO).",
|
|
2369
|
+
pitfalls: ["Reading '0' from a count-up that hasn't run — always prefer data-target/data-count.", "'1.2k' style abbreviations: store 1.2 with suffix 'k', not 1200, so the copy stays as written."]
|
|
2370
|
+
}
|
|
2371
|
+
},
|
|
2372
|
+
{
|
|
2373
|
+
id: "hero-logo-wall",
|
|
2374
|
+
previewLayout: "logos",
|
|
2375
|
+
label: "Hero with logo wall",
|
|
2376
|
+
proves: "Logos as a grid array with the company name as the accessible text · monochrome treatment and marquee as design",
|
|
2377
|
+
category: "Heroes",
|
|
2378
|
+
tags: [
|
|
2379
|
+
"repeater",
|
|
2380
|
+
"media-heavy",
|
|
2381
|
+
"a11y",
|
|
2382
|
+
"conditional",
|
|
2383
|
+
"variants"
|
|
2384
|
+
],
|
|
2385
|
+
brand: "Tideline",
|
|
2386
|
+
schema: {
|
|
2387
|
+
name: "logoWallHero",
|
|
2388
|
+
title: "Logo wall hero",
|
|
2389
|
+
kind: "block",
|
|
2390
|
+
icon: "badges",
|
|
2391
|
+
category: "Heroes",
|
|
2392
|
+
description: "A headline with a strip of customer logos under it.",
|
|
2393
|
+
variants: [{
|
|
2394
|
+
id: "strip",
|
|
2395
|
+
name: "Single strip"
|
|
2396
|
+
}, {
|
|
2397
|
+
id: "grid",
|
|
2398
|
+
name: "Grid",
|
|
2399
|
+
description: "Two or three rows of logos"
|
|
2400
|
+
}],
|
|
2401
|
+
preview: {
|
|
2402
|
+
title: "heading",
|
|
2403
|
+
subtitle: "logosLabel"
|
|
2404
|
+
},
|
|
2405
|
+
groups: GROUPS,
|
|
2406
|
+
fields: [
|
|
2407
|
+
heading(56),
|
|
2408
|
+
{
|
|
2409
|
+
name: "subheading",
|
|
2410
|
+
title: "Subheading",
|
|
2411
|
+
type: "text",
|
|
2412
|
+
rows: 2,
|
|
2413
|
+
validation: { recommendedMax: 160 },
|
|
2414
|
+
ai: true,
|
|
2415
|
+
localized: true
|
|
2416
|
+
},
|
|
2417
|
+
primary,
|
|
2418
|
+
secondary,
|
|
2419
|
+
{
|
|
2420
|
+
name: "logosLabel",
|
|
2421
|
+
title: "Logo row label",
|
|
2422
|
+
type: "string",
|
|
2423
|
+
validation: { max: 60 },
|
|
2424
|
+
placeholder: "Trusted by 4,000 engineering teams"
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
name: "logos",
|
|
2428
|
+
title: "Logos",
|
|
2429
|
+
type: "array",
|
|
2430
|
+
group: "media",
|
|
2431
|
+
itemNoun: "logo",
|
|
2432
|
+
arrayLayout: "grid",
|
|
2433
|
+
validation: {
|
|
2434
|
+
min: 4,
|
|
2435
|
+
max: 18
|
|
2436
|
+
},
|
|
2437
|
+
of: [{
|
|
2438
|
+
name: "customerLogo",
|
|
2439
|
+
title: "Logo",
|
|
2440
|
+
type: "object",
|
|
2441
|
+
icon: "building",
|
|
2442
|
+
preview: {
|
|
2443
|
+
title: "name",
|
|
2444
|
+
media: "logo",
|
|
2445
|
+
fallback: "Untitled logo"
|
|
2446
|
+
},
|
|
2447
|
+
fields: [
|
|
2448
|
+
{
|
|
2449
|
+
name: "name",
|
|
2450
|
+
title: "Company",
|
|
2451
|
+
type: "string",
|
|
2452
|
+
validation: {
|
|
2453
|
+
required: true,
|
|
2454
|
+
max: 40
|
|
2455
|
+
},
|
|
2456
|
+
description: "Not shown. Read by screen readers in place of the logo."
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
name: "logo",
|
|
2460
|
+
title: "Logo",
|
|
2461
|
+
type: "image",
|
|
2462
|
+
validation: { required: true },
|
|
2463
|
+
description: ""
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
name: "link",
|
|
2467
|
+
title: "Case study",
|
|
2468
|
+
type: "link"
|
|
2469
|
+
}
|
|
2470
|
+
]
|
|
2471
|
+
}]
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
name: "treatment",
|
|
2475
|
+
title: "Logo color",
|
|
2476
|
+
type: "select",
|
|
2477
|
+
group: "design",
|
|
2478
|
+
layout: "segmented",
|
|
2479
|
+
initialValue: "mono",
|
|
2480
|
+
options: [{
|
|
2481
|
+
value: "mono",
|
|
2482
|
+
label: "One color"
|
|
2483
|
+
}, {
|
|
2484
|
+
value: "original",
|
|
2485
|
+
label: "Original"
|
|
2486
|
+
}]
|
|
2487
|
+
},
|
|
2488
|
+
{
|
|
2489
|
+
name: "motion",
|
|
2490
|
+
title: "Motion",
|
|
2491
|
+
type: "select",
|
|
2492
|
+
group: "design",
|
|
2493
|
+
layout: "segmented",
|
|
2494
|
+
initialValue: "static",
|
|
2495
|
+
options: [{
|
|
2496
|
+
value: "static",
|
|
2497
|
+
label: "Still"
|
|
2498
|
+
}, {
|
|
2499
|
+
value: "marquee",
|
|
2500
|
+
label: "Scrolling"
|
|
2501
|
+
}]
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
name: "speed",
|
|
2505
|
+
title: "Scroll speed",
|
|
2506
|
+
type: "number",
|
|
2507
|
+
group: "design",
|
|
2508
|
+
layout: "slider",
|
|
2509
|
+
hidden: {
|
|
2510
|
+
field: "motion",
|
|
2511
|
+
equals: "marquee"
|
|
2512
|
+
},
|
|
2513
|
+
numeric: {
|
|
2514
|
+
min: 10,
|
|
2515
|
+
max: 60,
|
|
2516
|
+
step: 5,
|
|
2517
|
+
unit: "s per loop"
|
|
2518
|
+
},
|
|
2519
|
+
initialValue: 30
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
...theme,
|
|
2523
|
+
initialValue: "dark"
|
|
2524
|
+
}
|
|
2525
|
+
]
|
|
2526
|
+
},
|
|
2527
|
+
value: {
|
|
2528
|
+
_variant: "strip",
|
|
2529
|
+
heading: "Observability your on-call rotation will thank you for.",
|
|
2530
|
+
subheading: "Traces, logs and deploys in one timeline, with alerts that say what broke — not just that something did.",
|
|
2531
|
+
primaryButton: btn("Start free", "https://app.tideline.dev/signup", "primary"),
|
|
2532
|
+
secondaryButton: btn("See pricing", "/pricing", "link", { arrow: true }),
|
|
2533
|
+
logosLabel: "Trusted by 4,000 engineering teams",
|
|
2534
|
+
logos: [
|
|
2535
|
+
{
|
|
2536
|
+
_key: "l1",
|
|
2537
|
+
name: "Caldera",
|
|
2538
|
+
logo: pic("logo-caldera", "Caldera", 320, 120),
|
|
2539
|
+
link: {
|
|
2540
|
+
type: "page",
|
|
2541
|
+
href: "/customers/caldera",
|
|
2542
|
+
title: "Caldera case study"
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
_key: "l2",
|
|
2547
|
+
name: "Hearth & Ash",
|
|
2548
|
+
logo: pic("logo-hearth", "Hearth & Ash", 320, 120)
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
_key: "l3",
|
|
2552
|
+
name: "Open Water Trust",
|
|
2553
|
+
logo: pic("logo-owt", "Open Water Trust", 320, 120)
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
_key: "l4",
|
|
2557
|
+
name: "Northbound Studio",
|
|
2558
|
+
logo: pic("logo-northbound", "Northbound Studio", 320, 120)
|
|
2559
|
+
},
|
|
2560
|
+
{
|
|
2561
|
+
_key: "l5",
|
|
2562
|
+
name: "Ferrous Labs",
|
|
2563
|
+
logo: pic("logo-ferrous", "Ferrous Labs", 320, 120)
|
|
2564
|
+
},
|
|
2565
|
+
{
|
|
2566
|
+
_key: "l6",
|
|
2567
|
+
name: "Kestrel Freight",
|
|
2568
|
+
logo: pic("logo-kestrel", "Kestrel Freight", 320, 120),
|
|
2569
|
+
link: {
|
|
2570
|
+
type: "page",
|
|
2571
|
+
href: "/customers/kestrel",
|
|
2572
|
+
title: "Kestrel Freight case study"
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
],
|
|
2576
|
+
treatment: "mono",
|
|
2577
|
+
motion: "marquee",
|
|
2578
|
+
speed: 30,
|
|
2579
|
+
theme: "dark"
|
|
2580
|
+
},
|
|
2581
|
+
notes: {
|
|
2582
|
+
why: "Each logo is an object whose REQUIRED `name` is the accessible text; the image's alt help line is turned off (description: '') and the name is copied into alt, so authors aren't asked twice. That's the a11y rule converters most often break: logos with alt='logo'. Logos are an array in a grid layout so they read as tiles. One-color treatment and the marquee are Design — the same content can be still or scrolling. Speed only appears for the marquee. At scale (the same logos on pricing, about and case studies) this array should become references to a Customers collection.",
|
|
2583
|
+
sanity: "object `logoHero` { logos: array<object { name: string, logo: image, link }> or array<reference<customer>> }, treatment: string, motion: string, speed: number (hidden unless marquee).",
|
|
2584
|
+
htmlSignals: [
|
|
2585
|
+
"A row/grid of 4–18 small <img>/<svg> elements with similar heights and grayscale filters (filter: grayscale(1) / opacity)",
|
|
2586
|
+
"A small label above them ('Trusted by…', 'Used by teams at…')",
|
|
2587
|
+
"A duplicated track with @keyframes translateX — the marquee (logos appear twice in the DOM)",
|
|
2588
|
+
"Alt texts like 'logo', 'client-1' (weak) or company names (good)"
|
|
2589
|
+
],
|
|
2590
|
+
mcp: "Hero + a strip of logos → `logoWallHero`. Dedupe logos duplicated for the marquee (same src) and set motion 'marquee'. For each logo: name = alt if it's a real name, else the file name or <title> cleaned up, else ask — never 'logo'. grayscale filter or single-fill SVGs → treatment 'mono'. If the same set appears on 3+ pages, create a Customers collection and switch to references (report it).",
|
|
2591
|
+
pitfalls: ["Alt text 'logo' x12 — screen readers hear nothing useful. Name is required for that reason.", "Marquee without a pause control is a WCAG 2.2.2 problem; the renderer pauses on hover/focus and reduced motion."]
|
|
2592
|
+
}
|
|
2593
|
+
},
|
|
2594
|
+
{
|
|
2595
|
+
id: "hero-split-testimonial",
|
|
2596
|
+
previewLayout: "split",
|
|
2597
|
+
label: "Split hero with testimonial card",
|
|
2598
|
+
proves: "Pick from the Testimonials collection OR write one here — a conditional reference/inline pair inside an object",
|
|
2599
|
+
category: "Heroes",
|
|
2600
|
+
tags: [
|
|
2601
|
+
"references",
|
|
2602
|
+
"conditional",
|
|
2603
|
+
"variants",
|
|
2604
|
+
"media-heavy"
|
|
2605
|
+
],
|
|
2606
|
+
brand: "Caldera",
|
|
2607
|
+
schema: {
|
|
2608
|
+
name: "testimonialHero",
|
|
2609
|
+
title: "Testimonial hero",
|
|
2610
|
+
kind: "block",
|
|
2611
|
+
icon: "message-circle-2",
|
|
2612
|
+
category: "Heroes",
|
|
2613
|
+
description: "Copy and buttons beside a photo, with a customer quote card overlapping it.",
|
|
2614
|
+
variants: [{
|
|
2615
|
+
id: "overlap",
|
|
2616
|
+
name: "Card over photo"
|
|
2617
|
+
}, {
|
|
2618
|
+
id: "under-copy",
|
|
2619
|
+
name: "Card under the copy"
|
|
2620
|
+
}],
|
|
2621
|
+
preview: {
|
|
2622
|
+
title: "heading",
|
|
2623
|
+
subtitle: "eyebrow",
|
|
2624
|
+
media: "image"
|
|
2625
|
+
},
|
|
2626
|
+
groups: GROUPS,
|
|
2627
|
+
fields: [
|
|
2628
|
+
eyebrow,
|
|
2629
|
+
heading(),
|
|
2630
|
+
body(),
|
|
2631
|
+
primary,
|
|
2632
|
+
secondary,
|
|
2633
|
+
{
|
|
2634
|
+
name: "testimonial",
|
|
2635
|
+
title: "Testimonial",
|
|
2636
|
+
type: "object",
|
|
2637
|
+
icon: "quote",
|
|
2638
|
+
openMode: "drill",
|
|
2639
|
+
fields: [
|
|
2640
|
+
{
|
|
2641
|
+
name: "source",
|
|
2642
|
+
title: "Quote from",
|
|
2643
|
+
type: "select",
|
|
2644
|
+
layout: "segmented",
|
|
2645
|
+
initialValue: "collection",
|
|
2646
|
+
options: [{
|
|
2647
|
+
value: "collection",
|
|
2648
|
+
label: "Testimonials"
|
|
2649
|
+
}, {
|
|
2650
|
+
value: "custom",
|
|
2651
|
+
label: "Write one here"
|
|
2652
|
+
}]
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
name: "entry",
|
|
2656
|
+
title: "Testimonial",
|
|
2657
|
+
type: "reference",
|
|
2658
|
+
reference: { to: ["col_testimonials"] },
|
|
2659
|
+
hidden: {
|
|
2660
|
+
field: "source",
|
|
2661
|
+
equals: "collection"
|
|
2662
|
+
},
|
|
2663
|
+
validation: { required: true }
|
|
2664
|
+
},
|
|
2665
|
+
{
|
|
2666
|
+
name: "quote",
|
|
2667
|
+
title: "Quote",
|
|
2668
|
+
type: "text",
|
|
2669
|
+
rows: 3,
|
|
2670
|
+
hidden: {
|
|
2671
|
+
field: "source",
|
|
2672
|
+
equals: "custom"
|
|
2673
|
+
},
|
|
2674
|
+
validation: {
|
|
2675
|
+
required: true,
|
|
2676
|
+
recommendedMax: 160
|
|
2677
|
+
}
|
|
2678
|
+
},
|
|
2679
|
+
{
|
|
2680
|
+
name: "name",
|
|
2681
|
+
title: "Name",
|
|
2682
|
+
type: "string",
|
|
2683
|
+
hidden: {
|
|
2684
|
+
field: "source",
|
|
2685
|
+
equals: "custom"
|
|
2686
|
+
},
|
|
2687
|
+
validation: {
|
|
2688
|
+
required: true,
|
|
2689
|
+
max: 40
|
|
2690
|
+
}
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
name: "role",
|
|
2694
|
+
title: "Role",
|
|
2695
|
+
type: "string",
|
|
2696
|
+
hidden: {
|
|
2697
|
+
field: "source",
|
|
2698
|
+
equals: "custom"
|
|
2699
|
+
},
|
|
2700
|
+
validation: { max: 60 },
|
|
2701
|
+
placeholder: "Backcountry guide, Montana"
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
name: "photo",
|
|
2705
|
+
title: "Photo",
|
|
2706
|
+
type: "image",
|
|
2707
|
+
hidden: {
|
|
2708
|
+
field: "source",
|
|
2709
|
+
equals: "custom"
|
|
2710
|
+
}
|
|
2711
|
+
},
|
|
2712
|
+
{
|
|
2713
|
+
name: "rating",
|
|
2714
|
+
title: "Rating",
|
|
2715
|
+
type: "rating",
|
|
2716
|
+
hidden: {
|
|
2717
|
+
field: "source",
|
|
2718
|
+
equals: "custom"
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
]
|
|
2722
|
+
},
|
|
2723
|
+
{
|
|
2724
|
+
name: "image",
|
|
2725
|
+
title: "Image",
|
|
2726
|
+
type: "image",
|
|
2727
|
+
group: "media",
|
|
2728
|
+
validation: { required: true }
|
|
2729
|
+
},
|
|
2730
|
+
theme
|
|
2731
|
+
]
|
|
2732
|
+
},
|
|
2733
|
+
value: {
|
|
2734
|
+
_variant: "overlap",
|
|
2735
|
+
eyebrow: "Made in Bend, Oregon",
|
|
2736
|
+
heading: "Cookware that gets better every time it meets a fire.",
|
|
2737
|
+
body: "Every Caldera pan is cast, machined and seasoned by twelve people in one workshop — and guaranteed for life.",
|
|
2738
|
+
primaryButton: btn("Shop cookware", "/products", "primary", { arrow: true }),
|
|
2739
|
+
secondaryButton: btn("Visit the workshop", "/about#workshop", "link"),
|
|
2740
|
+
testimonial: {
|
|
2741
|
+
source: "collection",
|
|
2742
|
+
entry: "ts_maya"
|
|
2743
|
+
},
|
|
2744
|
+
image: pic("caldera-workshop", "A worker pouring molten iron into a skillet mould in the Bend workshop", 1600, 2e3),
|
|
2745
|
+
theme: "light"
|
|
2746
|
+
},
|
|
2747
|
+
notes: {
|
|
2748
|
+
why: "Social proof should usually come from ONE place (the Testimonials collection) so a quote that's withdrawn disappears everywhere. But landing pages often need a one-off quote, so `source` toggles between a reference and inline fields — never both visible, never both stored as the truth. The card is an object so the whole quote travels together when the variant changes from 'over the photo' to 'under the copy'.",
|
|
2749
|
+
sanity: "object `testimonialHero` { testimonial: object { source: string, entry: reference<testimonial> (hidden unless collection), quote, name, role, photo, rating (hidden unless custom) } } — Sanity's conditional `hidden` callback is identical in spirit.",
|
|
2750
|
+
htmlSignals: [
|
|
2751
|
+
"A <figure>/<blockquote> card with a photo, name and role positioned absolutely over the hero image",
|
|
2752
|
+
"Star icons or '★★★★★' next to the quote",
|
|
2753
|
+
"The same quote text appearing on a /reviews or /testimonials page — collection candidate"
|
|
2754
|
+
],
|
|
2755
|
+
mcp: "Hero with a quote card → `testimonialHero`. Search the Testimonials collection for the quote text (normalized, ≥ 90% match): found → source 'collection', entry = its id; not found → create the testimonial entry if the site has a testimonials page, else source 'custom' and fill quote/name/role/photo/rating. Card positioned over the image → variant 'overlap'.",
|
|
2756
|
+
pitfalls: ["Quotes typed inline on every landing page drift from the approved wording — prefer the collection."]
|
|
2757
|
+
}
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
id: "hero-event",
|
|
2761
|
+
previewLayout: "split",
|
|
2762
|
+
label: "Event hero",
|
|
2763
|
+
proves: "Date/time with zone · in-person/online/hybrid venue with conditional map or stream · sold-out swap · Event data from content",
|
|
2764
|
+
category: "Heroes",
|
|
2765
|
+
tags: [
|
|
2766
|
+
"conditional",
|
|
2767
|
+
"seo-structured",
|
|
2768
|
+
"validation",
|
|
2769
|
+
"media-heavy"
|
|
2770
|
+
],
|
|
2771
|
+
brand: "Open Water Trust",
|
|
2772
|
+
schema: {
|
|
2773
|
+
name: "eventHero",
|
|
2774
|
+
title: "Event hero",
|
|
2775
|
+
kind: "block",
|
|
2776
|
+
icon: "calendar-event",
|
|
2777
|
+
category: "Heroes",
|
|
2778
|
+
description: "Event name, date, place, a countdown and the register button.",
|
|
2779
|
+
preview: {
|
|
2780
|
+
title: "heading",
|
|
2781
|
+
subtitle: "startsAt",
|
|
2782
|
+
media: "image"
|
|
2783
|
+
},
|
|
2784
|
+
groups: [
|
|
2785
|
+
{
|
|
2786
|
+
name: "content",
|
|
2787
|
+
title: "Content",
|
|
2788
|
+
default: true
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
name: "when",
|
|
2792
|
+
title: "When & where"
|
|
2793
|
+
},
|
|
2794
|
+
{
|
|
2795
|
+
name: "tickets",
|
|
2796
|
+
title: "Tickets"
|
|
2797
|
+
},
|
|
2798
|
+
{
|
|
2799
|
+
name: "design",
|
|
2800
|
+
title: "Design"
|
|
2801
|
+
}
|
|
2802
|
+
],
|
|
2803
|
+
fields: [
|
|
2804
|
+
eyebrow,
|
|
2805
|
+
{
|
|
2806
|
+
...heading(48),
|
|
2807
|
+
title: "Event name"
|
|
2808
|
+
},
|
|
2809
|
+
body(160),
|
|
2810
|
+
{
|
|
2811
|
+
name: "image",
|
|
2812
|
+
title: "Image",
|
|
2813
|
+
type: "image",
|
|
2814
|
+
validation: { required: true }
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
name: "startsAt",
|
|
2818
|
+
title: "Starts",
|
|
2819
|
+
type: "datetime",
|
|
2820
|
+
group: "when",
|
|
2821
|
+
validation: { required: true },
|
|
2822
|
+
description: "In the event's own time zone. Visitors see it converted."
|
|
2823
|
+
},
|
|
2824
|
+
{
|
|
2825
|
+
name: "endsAt",
|
|
2826
|
+
title: "Ends",
|
|
2827
|
+
type: "datetime",
|
|
2828
|
+
group: "when"
|
|
2829
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
name: "format",
|
|
2832
|
+
title: "Format",
|
|
2833
|
+
type: "select",
|
|
2834
|
+
group: "when",
|
|
2835
|
+
layout: "segmented",
|
|
2836
|
+
initialValue: "in-person",
|
|
2837
|
+
options: [
|
|
2838
|
+
{
|
|
2839
|
+
value: "in-person",
|
|
2840
|
+
label: "In person"
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
value: "online",
|
|
2844
|
+
label: "Online"
|
|
2845
|
+
},
|
|
2846
|
+
{
|
|
2847
|
+
value: "hybrid",
|
|
2848
|
+
label: "Hybrid"
|
|
2849
|
+
}
|
|
2850
|
+
]
|
|
2851
|
+
},
|
|
2852
|
+
{
|
|
2853
|
+
name: "venue",
|
|
2854
|
+
title: "Venue",
|
|
2855
|
+
type: "string",
|
|
2856
|
+
group: "when",
|
|
2857
|
+
hidden: {
|
|
2858
|
+
field: "format",
|
|
2859
|
+
equals: ["in-person", "hybrid"]
|
|
2860
|
+
},
|
|
2861
|
+
validation: {
|
|
2862
|
+
required: true,
|
|
2863
|
+
max: 60
|
|
2864
|
+
}
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
name: "address",
|
|
2868
|
+
title: "Address",
|
|
2869
|
+
type: "text",
|
|
2870
|
+
group: "when",
|
|
2871
|
+
rows: 2,
|
|
2872
|
+
hidden: {
|
|
2873
|
+
field: "format",
|
|
2874
|
+
equals: ["in-person", "hybrid"]
|
|
2875
|
+
}
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
name: "location",
|
|
2879
|
+
title: "Map pin",
|
|
2880
|
+
type: "geopoint",
|
|
2881
|
+
group: "when",
|
|
2882
|
+
hidden: {
|
|
2883
|
+
field: "format",
|
|
2884
|
+
equals: ["in-person", "hybrid"]
|
|
2885
|
+
}
|
|
2886
|
+
},
|
|
2887
|
+
{
|
|
2888
|
+
name: "stream",
|
|
2889
|
+
title: "Stream link",
|
|
2890
|
+
type: "link",
|
|
2891
|
+
group: "when",
|
|
2892
|
+
hidden: {
|
|
2893
|
+
field: "format",
|
|
2894
|
+
equals: ["online", "hybrid"]
|
|
2895
|
+
},
|
|
2896
|
+
description: "Shown on the page only once the event starts."
|
|
2897
|
+
},
|
|
2898
|
+
{
|
|
2899
|
+
name: "showCountdown",
|
|
2900
|
+
title: "Show a countdown",
|
|
2901
|
+
type: "boolean",
|
|
2902
|
+
group: "when",
|
|
2903
|
+
initialValue: true
|
|
2904
|
+
},
|
|
2905
|
+
{
|
|
2906
|
+
name: "status",
|
|
2907
|
+
title: "Ticket status",
|
|
2908
|
+
type: "select",
|
|
2909
|
+
group: "tickets",
|
|
2910
|
+
layout: "radio",
|
|
2911
|
+
initialValue: "on-sale",
|
|
2912
|
+
options: [
|
|
2913
|
+
{
|
|
2914
|
+
value: "on-sale",
|
|
2915
|
+
label: "On sale"
|
|
2916
|
+
},
|
|
2917
|
+
{
|
|
2918
|
+
value: "few-left",
|
|
2919
|
+
label: "Few left",
|
|
2920
|
+
description: "Adds a ‘Few tickets left’ note"
|
|
2921
|
+
},
|
|
2922
|
+
{
|
|
2923
|
+
value: "sold-out",
|
|
2924
|
+
label: "Sold out",
|
|
2925
|
+
description: "Swaps the button for a waitlist"
|
|
2926
|
+
},
|
|
2927
|
+
{
|
|
2928
|
+
value: "cancelled",
|
|
2929
|
+
label: "Cancelled"
|
|
2930
|
+
}
|
|
2931
|
+
]
|
|
2932
|
+
},
|
|
2933
|
+
{
|
|
2934
|
+
name: "registerButton",
|
|
2935
|
+
title: "Register button",
|
|
2936
|
+
type: "button",
|
|
2937
|
+
group: "tickets",
|
|
2938
|
+
hidden: {
|
|
2939
|
+
field: "status",
|
|
2940
|
+
equals: ["on-sale", "few-left"]
|
|
2941
|
+
},
|
|
2942
|
+
validation: { required: true }
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
name: "price",
|
|
2946
|
+
title: "Price line",
|
|
2947
|
+
type: "string",
|
|
2948
|
+
group: "tickets",
|
|
2949
|
+
hidden: {
|
|
2950
|
+
field: "status",
|
|
2951
|
+
equals: ["on-sale", "few-left"]
|
|
2952
|
+
},
|
|
2953
|
+
validation: { max: 48 },
|
|
2954
|
+
placeholder: "From $150 · Tables of 8 available"
|
|
2955
|
+
},
|
|
2956
|
+
{
|
|
2957
|
+
name: "waitlistButton",
|
|
2958
|
+
title: "Waitlist button",
|
|
2959
|
+
type: "button",
|
|
2960
|
+
group: "tickets",
|
|
2961
|
+
hidden: {
|
|
2962
|
+
field: "status",
|
|
2963
|
+
equals: "sold-out"
|
|
2964
|
+
},
|
|
2965
|
+
validation: { required: true }
|
|
2966
|
+
},
|
|
2967
|
+
{
|
|
2968
|
+
name: "cancelNote",
|
|
2969
|
+
title: "Cancellation note",
|
|
2970
|
+
type: "text",
|
|
2971
|
+
group: "tickets",
|
|
2972
|
+
rows: 2,
|
|
2973
|
+
hidden: {
|
|
2974
|
+
field: "status",
|
|
2975
|
+
equals: "cancelled"
|
|
2976
|
+
},
|
|
2977
|
+
validation: {
|
|
2978
|
+
required: true,
|
|
2979
|
+
max: 200
|
|
2980
|
+
}
|
|
2981
|
+
},
|
|
2982
|
+
overlay,
|
|
2983
|
+
{
|
|
2984
|
+
...theme,
|
|
2985
|
+
initialValue: "dark"
|
|
2986
|
+
}
|
|
2987
|
+
]
|
|
2988
|
+
},
|
|
2989
|
+
value: {
|
|
2990
|
+
eyebrow: "Annual benefit",
|
|
2991
|
+
heading: "Headwaters Gala 2026",
|
|
2992
|
+
body: "An evening on the river for the people who keep it running — dinner by Hearth & Ash, a film premiere and a live auction.",
|
|
2993
|
+
image: pic("owt-gala", "Long tables set on a riverbank at sunset with lanterns", 2400, 1400),
|
|
2994
|
+
startsAt: "2026-11-14T18:00:00-08:00[America/Los_Angeles]",
|
|
2995
|
+
endsAt: "2026-11-14T22:30:00-08:00[America/Los_Angeles]",
|
|
2996
|
+
format: "hybrid",
|
|
2997
|
+
venue: "The Old Mill at Iron Gate",
|
|
2998
|
+
address: "5000 Copco Rd\nHornbrook, CA 96044",
|
|
2999
|
+
location: {
|
|
3000
|
+
label: "The Old Mill at Iron Gate",
|
|
3001
|
+
address: "5000 Copco Rd, Hornbrook, CA 96044",
|
|
3002
|
+
lat: 41.931,
|
|
3003
|
+
lng: -122.437
|
|
3004
|
+
},
|
|
3005
|
+
stream: {
|
|
3006
|
+
type: "url",
|
|
3007
|
+
href: "https://live.openwatertrust.org/gala-2026",
|
|
3008
|
+
newTab: true
|
|
3009
|
+
},
|
|
3010
|
+
showCountdown: true,
|
|
3011
|
+
status: "few-left",
|
|
3012
|
+
registerButton: btn("Get tickets", "https://openwatertrust.org/gala/tickets", "primary"),
|
|
3013
|
+
price: "From $150 · Tables of 8 available",
|
|
3014
|
+
waitlistButton: btn("Join the waitlist", "/gala/waitlist", "outline"),
|
|
3015
|
+
overlay: 50,
|
|
3016
|
+
theme: "dark"
|
|
3017
|
+
},
|
|
3018
|
+
notes: {
|
|
3019
|
+
why: "The event's FACTS (start/end with time zone, format, venue, map pin, stream) are structured because the same values drive the countdown, the add-to-calendar file and native Event JSON-LD — no separate SEO fields. Format decides which place fields show (venue/address/pin for in-person, stream for online, both for hybrid). Ticket status is one select that swaps the button: on sale → registerButton, sold out → waitlistButton, cancelled → a required note. If the site lists many events, these fields belong on an Events collection entry and the hero becomes a single reference (see pitfalls).",
|
|
3020
|
+
sanity: "object `eventHero` { startsAt/endsAt: datetime (Sanity stores UTC; zone needs a separate field), format: string, venue, address, location: geopoint, stream: url, status: string, registerButton/waitlistButton: object button, cancelNote: text } — with custom validation `Rule.custom` for endsAt > startsAt.",
|
|
3021
|
+
htmlSignals: [
|
|
3022
|
+
"A hero with a date/time line (<time datetime=…>), a place line and a 'Register'/'Get tickets' button",
|
|
3023
|
+
"A ticking countdown (days/hours/minutes) bound to a data-date attribute",
|
|
3024
|
+
"Ticketing links (eventbrite.com, tito.io, lu.ma) or a 'Sold out' badge",
|
|
3025
|
+
"JSON-LD @type Event with eventAttendanceMode (Offline/Online/Mixed) and location/VirtualLocation",
|
|
3026
|
+
"An 'Add to calendar' link or .ics download"
|
|
3027
|
+
],
|
|
3028
|
+
mcp: "Hero with a date + place + ticket button → `eventHero`. <time datetime> or the countdown's data-date → startsAt with its offset (ask for the zone if the source has none). JSON-LD eventAttendanceMode → format (Offline → in-person, Online → online, Mixed → hybrid); location.name/address/geo → venue/address/location. Ticket link → registerButton; 'Sold out' text → status 'sold-out'. Never model the add-to-calendar link — it's generated. If the page is one of many event pages, create an Events collection and report it.",
|
|
3029
|
+
pitfalls: ["Dates stored as text ('Nov 14, 6pm') can't drive the countdown or JSON-LD — always a datetime with offset.", "End before start isn't caught today (gap: cross-field validation)."]
|
|
3030
|
+
}
|
|
3031
|
+
},
|
|
3032
|
+
{
|
|
3033
|
+
id: "hero-personalized",
|
|
3034
|
+
previewLayout: "split",
|
|
3035
|
+
label: "Personalized hero (audiences + A/B)",
|
|
3036
|
+
proves: "Default content + audience versions that override only what they set · rule types with conditional inputs · an experiment split",
|
|
3037
|
+
category: "Heroes",
|
|
3038
|
+
tags: [
|
|
3039
|
+
"personalization",
|
|
3040
|
+
"conditional",
|
|
3041
|
+
"repeater",
|
|
3042
|
+
"validation",
|
|
3043
|
+
"deep-nesting"
|
|
3044
|
+
],
|
|
3045
|
+
brand: "Tideline",
|
|
3046
|
+
schema: {
|
|
3047
|
+
name: "audienceHero",
|
|
3048
|
+
title: "Audience hero",
|
|
3049
|
+
kind: "block",
|
|
3050
|
+
icon: "target",
|
|
3051
|
+
category: "Heroes",
|
|
3052
|
+
description: "One hero with versions for different audiences, and an optional A/B test.",
|
|
3053
|
+
preview: {
|
|
3054
|
+
title: "heading",
|
|
3055
|
+
subtitle: "eyebrow",
|
|
3056
|
+
media: "image"
|
|
3057
|
+
},
|
|
3058
|
+
groups: [
|
|
3059
|
+
{
|
|
3060
|
+
name: "content",
|
|
3061
|
+
title: "Everyone",
|
|
3062
|
+
default: true
|
|
3063
|
+
},
|
|
3064
|
+
{
|
|
3065
|
+
name: "audiences",
|
|
3066
|
+
title: "Audiences"
|
|
3067
|
+
},
|
|
3068
|
+
{
|
|
3069
|
+
name: "experiment",
|
|
3070
|
+
title: "Experiment"
|
|
3071
|
+
},
|
|
3072
|
+
{
|
|
3073
|
+
name: "design",
|
|
3074
|
+
title: "Design"
|
|
3075
|
+
}
|
|
3076
|
+
],
|
|
3077
|
+
fields: [
|
|
3078
|
+
eyebrow,
|
|
3079
|
+
heading(),
|
|
3080
|
+
body(),
|
|
3081
|
+
primary,
|
|
3082
|
+
{
|
|
3083
|
+
name: "image",
|
|
3084
|
+
title: "Image",
|
|
3085
|
+
type: "image",
|
|
3086
|
+
group: "content"
|
|
3087
|
+
},
|
|
3088
|
+
{
|
|
3089
|
+
name: "audiences",
|
|
3090
|
+
title: "Audience versions",
|
|
3091
|
+
type: "array",
|
|
3092
|
+
group: "audiences",
|
|
3093
|
+
itemNoun: "audience",
|
|
3094
|
+
validation: { max: 5 },
|
|
3095
|
+
description: "Checked top to bottom; the first match wins.",
|
|
3096
|
+
of: [{
|
|
3097
|
+
name: "audience",
|
|
3098
|
+
title: "Audience",
|
|
3099
|
+
type: "object",
|
|
3100
|
+
icon: "user-search",
|
|
3101
|
+
preview: {
|
|
3102
|
+
title: "name",
|
|
3103
|
+
subtitle: "rule.type",
|
|
3104
|
+
fallback: "Untitled audience"
|
|
3105
|
+
},
|
|
3106
|
+
fields: [
|
|
3107
|
+
{
|
|
3108
|
+
name: "name",
|
|
3109
|
+
title: "Name",
|
|
3110
|
+
type: "string",
|
|
3111
|
+
validation: {
|
|
3112
|
+
required: true,
|
|
3113
|
+
max: 40
|
|
3114
|
+
},
|
|
3115
|
+
description: "Only editors see this."
|
|
3116
|
+
},
|
|
3117
|
+
{
|
|
3118
|
+
name: "rule",
|
|
3119
|
+
title: "Who sees it",
|
|
3120
|
+
type: "object",
|
|
3121
|
+
icon: "filter",
|
|
3122
|
+
openMode: "drill",
|
|
3123
|
+
fields: [
|
|
3124
|
+
{
|
|
3125
|
+
name: "type",
|
|
3126
|
+
title: "Match by",
|
|
3127
|
+
type: "select",
|
|
3128
|
+
validation: { required: true },
|
|
3129
|
+
options: [
|
|
3130
|
+
{
|
|
3131
|
+
value: "utm_campaign",
|
|
3132
|
+
label: "Ad campaign (utm_campaign)"
|
|
3133
|
+
},
|
|
3134
|
+
{
|
|
3135
|
+
value: "referrer",
|
|
3136
|
+
label: "Came from a site"
|
|
3137
|
+
},
|
|
3138
|
+
{
|
|
3139
|
+
value: "country",
|
|
3140
|
+
label: "Country"
|
|
3141
|
+
},
|
|
3142
|
+
{
|
|
3143
|
+
value: "returning",
|
|
3144
|
+
label: "Returning visitor",
|
|
3145
|
+
description: "Visited in the last 30 days"
|
|
3146
|
+
},
|
|
3147
|
+
{
|
|
3148
|
+
value: "segment",
|
|
3149
|
+
label: "CRM segment"
|
|
3150
|
+
}
|
|
3151
|
+
]
|
|
3152
|
+
},
|
|
3153
|
+
{
|
|
3154
|
+
name: "match",
|
|
3155
|
+
title: "Contains",
|
|
3156
|
+
type: "string",
|
|
3157
|
+
hidden: {
|
|
3158
|
+
field: "type",
|
|
3159
|
+
equals: ["utm_campaign", "referrer"]
|
|
3160
|
+
},
|
|
3161
|
+
validation: {
|
|
3162
|
+
required: true,
|
|
3163
|
+
max: 80
|
|
3164
|
+
},
|
|
3165
|
+
placeholder: "k8s-launch"
|
|
3166
|
+
},
|
|
3167
|
+
{
|
|
3168
|
+
name: "countries",
|
|
3169
|
+
title: "Countries",
|
|
3170
|
+
type: "multiselect",
|
|
3171
|
+
hidden: {
|
|
3172
|
+
field: "type",
|
|
3173
|
+
equals: "country"
|
|
3174
|
+
},
|
|
3175
|
+
validation: { min: 1 },
|
|
3176
|
+
options: [
|
|
3177
|
+
{
|
|
3178
|
+
value: "US",
|
|
3179
|
+
label: "United States"
|
|
3180
|
+
},
|
|
3181
|
+
{
|
|
3182
|
+
value: "GB",
|
|
3183
|
+
label: "United Kingdom"
|
|
3184
|
+
},
|
|
3185
|
+
{
|
|
3186
|
+
value: "DE",
|
|
3187
|
+
label: "Germany"
|
|
3188
|
+
},
|
|
3189
|
+
{
|
|
3190
|
+
value: "IN",
|
|
3191
|
+
label: "India"
|
|
3192
|
+
},
|
|
3193
|
+
{
|
|
3194
|
+
value: "JP",
|
|
3195
|
+
label: "Japan"
|
|
3196
|
+
}
|
|
3197
|
+
]
|
|
3198
|
+
},
|
|
3199
|
+
{
|
|
3200
|
+
name: "segment",
|
|
3201
|
+
title: "Segment",
|
|
3202
|
+
type: "select",
|
|
3203
|
+
hidden: {
|
|
3204
|
+
field: "type",
|
|
3205
|
+
equals: "segment"
|
|
3206
|
+
},
|
|
3207
|
+
validation: { required: true },
|
|
3208
|
+
options: [
|
|
3209
|
+
{
|
|
3210
|
+
value: "enterprise",
|
|
3211
|
+
label: "Enterprise accounts (1,000+ staff)"
|
|
3212
|
+
},
|
|
3213
|
+
{
|
|
3214
|
+
value: "trial",
|
|
3215
|
+
label: "Active trials"
|
|
3216
|
+
},
|
|
3217
|
+
{
|
|
3218
|
+
value: "churned",
|
|
3219
|
+
label: "Cancelled in the last 90 days"
|
|
3220
|
+
}
|
|
3221
|
+
]
|
|
3222
|
+
}
|
|
3223
|
+
]
|
|
3224
|
+
},
|
|
3225
|
+
{
|
|
3226
|
+
name: "eyebrow",
|
|
3227
|
+
title: "Eyebrow",
|
|
3228
|
+
type: "string",
|
|
3229
|
+
validation: { max: 40 },
|
|
3230
|
+
description: "Leave empty to keep the Everyone version."
|
|
3231
|
+
},
|
|
3232
|
+
{
|
|
3233
|
+
name: "heading",
|
|
3234
|
+
title: "Heading",
|
|
3235
|
+
type: "string",
|
|
3236
|
+
validation: {
|
|
3237
|
+
max: 100,
|
|
3238
|
+
recommendedMax: 60
|
|
3239
|
+
},
|
|
3240
|
+
ai: true,
|
|
3241
|
+
description: "Leave empty to keep the Everyone version."
|
|
3242
|
+
},
|
|
3243
|
+
{
|
|
3244
|
+
name: "body",
|
|
3245
|
+
title: "Body",
|
|
3246
|
+
type: "text",
|
|
3247
|
+
rows: 3,
|
|
3248
|
+
validation: { recommendedMax: 180 },
|
|
3249
|
+
ai: true,
|
|
3250
|
+
description: "Leave empty to keep the Everyone version."
|
|
3251
|
+
},
|
|
3252
|
+
{
|
|
3253
|
+
name: "primaryButton",
|
|
3254
|
+
title: "Primary button",
|
|
3255
|
+
type: "button",
|
|
3256
|
+
description: "Leave empty to keep the Everyone version."
|
|
3257
|
+
},
|
|
3258
|
+
{
|
|
3259
|
+
name: "image",
|
|
3260
|
+
title: "Image",
|
|
3261
|
+
type: "image",
|
|
3262
|
+
description: "Leave empty to keep the Everyone version."
|
|
3263
|
+
}
|
|
3264
|
+
]
|
|
3265
|
+
}]
|
|
3266
|
+
},
|
|
3267
|
+
{
|
|
3268
|
+
name: "test",
|
|
3269
|
+
title: "A/B test",
|
|
3270
|
+
type: "object",
|
|
3271
|
+
group: "experiment",
|
|
3272
|
+
icon: "flask",
|
|
3273
|
+
openMode: "drill",
|
|
3274
|
+
fields: [
|
|
3275
|
+
{
|
|
3276
|
+
name: "isRunning",
|
|
3277
|
+
title: "Run a test",
|
|
3278
|
+
type: "boolean"
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
name: "share",
|
|
3282
|
+
title: "Share seeing the new version",
|
|
3283
|
+
type: "number",
|
|
3284
|
+
layout: "slider",
|
|
3285
|
+
hidden: {
|
|
3286
|
+
field: "isRunning",
|
|
3287
|
+
equals: true
|
|
3288
|
+
},
|
|
3289
|
+
numeric: {
|
|
3290
|
+
min: 10,
|
|
3291
|
+
max: 90,
|
|
3292
|
+
step: 5,
|
|
3293
|
+
unit: "%"
|
|
3294
|
+
},
|
|
3295
|
+
initialValue: 50
|
|
3296
|
+
},
|
|
3297
|
+
{
|
|
3298
|
+
name: "goal",
|
|
3299
|
+
title: "Success means",
|
|
3300
|
+
type: "select",
|
|
3301
|
+
hidden: {
|
|
3302
|
+
field: "isRunning",
|
|
3303
|
+
equals: true
|
|
3304
|
+
},
|
|
3305
|
+
validation: { required: true },
|
|
3306
|
+
options: [
|
|
3307
|
+
{
|
|
3308
|
+
value: "primary-click",
|
|
3309
|
+
label: "Clicked the primary button"
|
|
3310
|
+
},
|
|
3311
|
+
{
|
|
3312
|
+
value: "signup",
|
|
3313
|
+
label: "Started a trial"
|
|
3314
|
+
},
|
|
3315
|
+
{
|
|
3316
|
+
value: "demo",
|
|
3317
|
+
label: "Booked a demo"
|
|
3318
|
+
}
|
|
3319
|
+
]
|
|
3320
|
+
},
|
|
3321
|
+
{
|
|
3322
|
+
name: "endsOn",
|
|
3323
|
+
title: "Stop on",
|
|
3324
|
+
type: "date",
|
|
3325
|
+
hidden: {
|
|
3326
|
+
field: "isRunning",
|
|
3327
|
+
equals: true
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
]
|
|
3331
|
+
},
|
|
3332
|
+
theme
|
|
3333
|
+
]
|
|
3334
|
+
},
|
|
3335
|
+
value: {
|
|
3336
|
+
eyebrow: "Observability for on-call teams",
|
|
3337
|
+
heading: "See why production is slow in one view, not twelve tabs.",
|
|
3338
|
+
body: "Tideline links traces, logs and deploys so the person holding the pager finds the cause in minutes.",
|
|
3339
|
+
primaryButton: btn("Start free", "https://app.tideline.dev/signup", "primary"),
|
|
3340
|
+
image: pic("tideline-timeline", "Tideline timeline showing a latency spike right after a deploy", 2400, 1500),
|
|
3341
|
+
audiences: [
|
|
3342
|
+
{
|
|
3343
|
+
_key: "a1",
|
|
3344
|
+
name: "Kubernetes ad campaign",
|
|
3345
|
+
rule: {
|
|
3346
|
+
type: "utm_campaign",
|
|
3347
|
+
match: "k8s-launch"
|
|
3348
|
+
},
|
|
3349
|
+
eyebrow: "New: Kubernetes traces",
|
|
3350
|
+
heading: "Every pod, every span, one timeline.",
|
|
3351
|
+
primaryButton: btn("Try it on your cluster", "https://app.tideline.dev/signup?template=k8s", "primary")
|
|
3352
|
+
},
|
|
3353
|
+
{
|
|
3354
|
+
_key: "a2",
|
|
3355
|
+
name: "Enterprise visitors",
|
|
3356
|
+
rule: {
|
|
3357
|
+
type: "segment",
|
|
3358
|
+
segment: "enterprise"
|
|
3359
|
+
},
|
|
3360
|
+
heading: "Observability that passes your security review.",
|
|
3361
|
+
body: "SOC 2 Type II, SSO and SCIM, EU data residency and a named engineer from day one.",
|
|
3362
|
+
primaryButton: btn("Talk to sales", "/contact-sales", "primary")
|
|
3363
|
+
},
|
|
3364
|
+
{
|
|
3365
|
+
_key: "a3",
|
|
3366
|
+
name: "Returning visitors",
|
|
3367
|
+
rule: { type: "returning" },
|
|
3368
|
+
eyebrow: "Welcome back",
|
|
3369
|
+
primaryButton: btn("Pick up your trial", "https://app.tideline.dev/login", "primary")
|
|
3370
|
+
}
|
|
3371
|
+
],
|
|
3372
|
+
test: {
|
|
3373
|
+
isRunning: true,
|
|
3374
|
+
share: 50,
|
|
3375
|
+
goal: "signup",
|
|
3376
|
+
endsOn: "2026-11-01"
|
|
3377
|
+
},
|
|
3378
|
+
theme: "dark"
|
|
3379
|
+
},
|
|
3380
|
+
notes: {
|
|
3381
|
+
why: "Personalization is modelled as ONE hero with a default ('Everyone') and an ordered array of audience versions that override only the fields they fill — so a returning-visitor version can change just the button. Each audience holds a rule object whose inputs depend on the rule type (a string for UTM/referrer, countries for geo, a segment select for CRM), making Hero › Audiences › Audience › Rule a four-level drill. The A/B test is a separate object in its own tab; its settings only appear when it's on. Overrides are plain optional fields with 'leave empty to keep' help, because the schema can't yet express inherit-from-default.",
|
|
3382
|
+
sanity: "object `audienceHero` { default: object hero, audiences: array<object { name, rule: object { type, match, countries, segment }, overrides: object hero (all optional) }>, test: object { enabled, share, goal, endsOn } } — teams often hand this to Sanity's Personalization/experiments plugins or an external tool (LaunchDarkly, Ninetailed) and store only variant ids.",
|
|
3383
|
+
htmlSignals: [
|
|
3384
|
+
"Server-rendered HTML differs between crawls with different ?utm_campaign= or Accept-Language/geo — compare responses",
|
|
3385
|
+
"Multiple hidden hero variants in the DOM with data-variant / data-audience attributes",
|
|
3386
|
+
"Experiment SDKs: Optimizely, VWO, Google Optimize remnants, LaunchDarkly, Ninetailed, Statsig (window.optimizely, _vwo_code)",
|
|
3387
|
+
"Flicker-prevention snippets (async-hide, opacity:0 on body until the SDK decides)"
|
|
3388
|
+
],
|
|
3389
|
+
mcp: "Only mark a hero personalized with evidence: hidden variants in the DOM or different responses for different UTM/geo requests. The variant shown with no parameters → the Everyone fields. Each other variant → audiences[] with ONLY the fields that differ (leave the rest empty), rule from the parameter that triggered it. Experiment SDK config (traffic allocation, goal event) → test. Never import third-party experiment ids as content; report the SDK so the team can decide whether BetterCMS replaces it.",
|
|
3390
|
+
pitfalls: [
|
|
3391
|
+
"Copying the whole hero into every audience — later edits to Everyone no longer reach them. Store only the differences.",
|
|
3392
|
+
"Two audiences that both match: order decides; the converter must preserve the source's precedence.",
|
|
3393
|
+
"Personalized copy must not be indexed as the canonical content — crawlers see Everyone."
|
|
3394
|
+
]
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
];
|
|
3398
|
+
//#endregion
|
|
3399
|
+
export { ENTRIES };
|
|
3400
|
+
|
|
3401
|
+
//# sourceMappingURL=heroes.entries.js.map
|