65 lines
7.7 KiB
JavaScript
65 lines
7.7 KiB
JavaScript
/**
|
|
* Curated, human-written facts the editor's code cannot express: a one-line
|
|
* purpose per component, and short usage tips where the real behaviour is
|
|
* surprising. Everything else in the catalog (props, types, enums, defaults,
|
|
* animation, icon support, child rules) is derived from the code. Adding a
|
|
* component to the resolver without an entry here fails the generator.
|
|
*/
|
|
export const NOTES = {
|
|
Accordion: { purpose: 'Expand/collapse FAQ list (items: title + content, isOpen)' },
|
|
BackgroundSection: { purpose: 'Full-width section over a bg image/colour with a dark overlay; children sit inside', tips: 'Text over bgImage needs overlayColor+overlayOpacity (>=0.45) for contrast.' },
|
|
BookingEmbed: { purpose: 'Embedded booking widget (calendly|square|custom url)', tips: 'Only if the user supplied a booking URL.' },
|
|
ButtonLink: { purpose: 'Link styled as a button; look comes from style (backgroundColor, color, padding, borderRadius, fontWeight)', tips: 'No variant prop: set style yourself, e.g. primary = {backgroundColor:"var(--wsb-primary)",color:"var(--wsb-bg)",padding:"14px 28px",borderRadius:"var(--wsb-button-radius)",fontWeight:"600"}; secondary = transparent bg + border.' },
|
|
CTASection: { purpose: 'Centered call-to-action band on a gradient (prop gradient = CSS gradient string); ctas[] buttons', tips: 'Use ctas[], not buttonText/buttonHref.' },
|
|
CallToAction: { purpose: 'Call-to-action band on colour/gradient/image (bgType+bgValue, overlay); ctas[] buttons', tips: 'Use ctas[], not buttonText/secondaryButton*.' },
|
|
ColumnLayout: { purpose: 'Row of columns; each direct child becomes one column and stacks on mobile', tips: 'ALWAYS set style display:"flex" and flexDirection:"row": published pages stack columns on phones ONLY via that inline pair (measured; without it columns stay side by side, squeezed). columns follows the child count; split must be an allowed string; style.alignItems:"center" centres vertically.' },
|
|
ContactForm: { purpose: 'Complete contact form with fields[], submit button, spam captcha option (no children)', tips: 'Delivery is configured by the owner (recipientEmail / webhook): never invent an address; leave recipientEmail empty.' },
|
|
Container: { purpose: 'Generic block (flow layout). Children stack vertically; use style for padding/background/maxWidth/flex', tips: 'tag is one of div|section|article|header|footer|main. The page root is a Container.' },
|
|
ContentSlider: { purpose: 'Auto-playing slider of content/image slides', tips: 'Use sparingly; a static hero is almost always better.' },
|
|
Countdown: { purpose: 'Countdown to targetDate (YYYY-MM-DD)', tips: 'Only for a real, user-given date.' },
|
|
Divider: { purpose: 'Horizontal rule' },
|
|
FeaturesGrid: { purpose: 'Responsive grid of cards {icon,title,description,image?,buttonText?,buttonUrl?}', tips: 'icon is PLAIN TEXT (a single glyph/emoji), NOT Font Awesome: never put fa-* there. For Font Awesome icon cards build ColumnLayout > Container > Icon + Heading + TextBlock instead. An image per feature (image,imageAlt) replaces the icon.' },
|
|
Footer: { purpose: 'Simple footer text line (copyright). For richer footers compose Container/ColumnLayout/SocialLinks/Menu', tips: 'Colour comes from style.color (default is dark text: set a light color on a dark footer). Emit in the top-level "footer" slot for scope=site.' },
|
|
FormButton: { purpose: 'Submit button inside a FormContainer' },
|
|
FormContainer: { purpose: 'Wrapper for hand-built forms (InputField/TextareaField/FormButton children)', tips: 'Prefer ContactForm.' },
|
|
Gallery: { purpose: 'Image grid (images[] {src,alt,caption?}), columns, optional lightbox', tips: 'Use the user\'s uploaded images only; no placeholders in galleries.' },
|
|
Heading: { purpose: 'Heading h1-h6; size/weight/colour/letterSpacing via style', tips: 'One h1 per page. Set fontFamily:"var(--wsb-heading-font)" and a size from the type scale; leave html empty.' },
|
|
HeroSimple: { purpose: 'Pre-built hero: heading, subtitle, ctas[], bg colour/gradient/image/video with overlay, minHeight', tips: 'With bgType=image set overlayColor+overlayOpacity (>=0.45) so light text passes contrast. No entrance animation support.' },
|
|
HtmlBlock: { purpose: 'Sanitised raw HTML. Last resort only', tips: 'Scripts/handlers are stripped.' },
|
|
Icon: { purpose: 'One Font Awesome icon (icon="fa-solid fa-<name>" or bare "fa-<name>"), optional circle/rounded background', tips: 'The ONLY real-icon component. size/color/bgColor/bgShape/bgSize; link optional.' },
|
|
ImageBlock: { purpose: 'Image. Crop/size/radius via style (width, maxWidth, aspectRatio, objectFit, borderRadius)', tips: 'Always set alt. Use style.aspectRatio with objectFit:"cover" (e.g. "4 / 3", "16 / 9", "1 / 1") and a consistent borderRadius. src: "assets/<file>" for uploaded images.' },
|
|
InputField: { purpose: 'Single-line form input inside FormContainer' },
|
|
Logo: { purpose: 'Text or image logo linking home' },
|
|
MapEmbed: { purpose: 'Embedded map for an address', tips: 'Only with a real address from the user.' },
|
|
Menu: { purpose: 'Link list (horizontal/vertical) with optional CTA-styled link' },
|
|
Navbar: { purpose: 'Top navigation bar: logo (text|image), links[] {text,href,isCta?}, sticky option', tips: 'Set showMobileMenu:true or the links overflow on phones. Emit in the top-level "header" slot. In-page links use href "#anchor"; other pages "/page-slug".' },
|
|
NumberCounter: { purpose: 'Row of animated stats (counters[] {number,suffix,label})', tips: 'Only real figures supplied by the user.' },
|
|
OpeningHours: { purpose: 'Opening hours table (rows[] {day,hours,closed})', tips: 'Only real hours supplied by the user.' },
|
|
PricingTable: { purpose: 'Pricing cards (plans[] {name,price,period,features[],buttonText,buttonHref,isFeatured})', tips: 'Only real prices supplied by the user; otherwise use a "Request a quote" CTA.' },
|
|
ReviewsStrip: { purpose: 'Strip of customer reviews (reviews[] {name,rating,text,source,url}) with link to all reviews', tips: 'Only real reviews supplied by the user.' },
|
|
SearchBar: { purpose: 'Search form' },
|
|
Section: { purpose: 'Full-width section band with a centred inner column (innerMaxWidth); optional top/bottom divider shapes', tips: 'Backgrounds and vertical padding go in style (backgroundColor, paddingTop/paddingBottom).' },
|
|
SocialLinks: { purpose: 'Row of social icons (links[] {platform,url})', tips: 'Only profiles the user gave.' },
|
|
Spacer: { purpose: 'Vertical gap' },
|
|
StarRating: { purpose: 'Star rating glyphs (rating 0-5)', tips: 'Only real ratings.' },
|
|
StickyCallBar: { purpose: 'Mobile-only sticky call/directions bar', tips: 'Only with a real phone number.' },
|
|
SubscribeForm: { purpose: 'Email newsletter signup' },
|
|
Tabs: { purpose: 'Tabbed content (tabs[] {label,content})' },
|
|
Testimonials: { purpose: 'Testimonial cards (testimonials[] {quote,name,title,rating}) in a grid or single layout', tips: 'Only real quotes supplied by the user.' },
|
|
TextBlock: { purpose: 'Paragraph text; size/colour/lineHeight/maxWidth via style', tips: 'Set fontFamily:"var(--wsb-body-font)", lineHeight 1.6-1.75, maxWidth about 65ch for long copy; leave html empty.' },
|
|
TextareaField: { purpose: 'Multi-line form input inside FormContainer' },
|
|
VideoBlock: { purpose: 'Video (file/youtube/vimeo) or background video', tips: 'Only with a URL the user gave.' },
|
|
};
|
|
|
|
export const GLOBAL_NOTES = {
|
|
common: {
|
|
rules: [
|
|
'Every prop below that is a colour accepts a hex, rgb()/hsl(), a gradient (where noted) or a design token var(--wsb-<name>).',
|
|
'Entrance animation (animation, animationDelay) exists only on components marked animation:true; HeroSimple, Divider, Spacer, HtmlBlock do not support it.',
|
|
],
|
|
},
|
|
icons: {
|
|
components: 'Icon renders Font Awesome. FeaturesGrid.icon is plain text, not Font Awesome.',
|
|
},
|
|
};
|