diff --git a/public/demo/gallery-campus.svg b/public/demo/gallery-campus.svg
new file mode 100644
index 0000000..cec6324
--- /dev/null
+++ b/public/demo/gallery-campus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-coast.svg b/public/demo/gallery-coast.svg
new file mode 100644
index 0000000..a315d97
--- /dev/null
+++ b/public/demo/gallery-coast.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-pylons.svg b/public/demo/gallery-pylons.svg
new file mode 100644
index 0000000..287ac3c
--- /dev/null
+++ b/public/demo/gallery-pylons.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-quarry.svg b/public/demo/gallery-quarry.svg
new file mode 100644
index 0000000..0d3e520
--- /dev/null
+++ b/public/demo/gallery-quarry.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-ridge.svg b/public/demo/gallery-ridge.svg
new file mode 100644
index 0000000..74de124
--- /dev/null
+++ b/public/demo/gallery-ridge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-roofs.svg b/public/demo/gallery-roofs.svg
new file mode 100644
index 0000000..cc410cd
--- /dev/null
+++ b/public/demo/gallery-roofs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-substation.svg b/public/demo/gallery-substation.svg
new file mode 100644
index 0000000..c1d128f
--- /dev/null
+++ b/public/demo/gallery-substation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/gallery-tower.svg b/public/demo/gallery-tower.svg
new file mode 100644
index 0000000..0a3b7f7
--- /dev/null
+++ b/public/demo/gallery-tower.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-bluepeak.svg b/public/demo/logo-bluepeak.svg
new file mode 100644
index 0000000..a86f109
--- /dev/null
+++ b/public/demo/logo-bluepeak.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-cedar.svg b/public/demo/logo-cedar.svg
new file mode 100644
index 0000000..24b2a0d
--- /dev/null
+++ b/public/demo/logo-cedar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-harbor.svg b/public/demo/logo-harbor.svg
new file mode 100644
index 0000000..3a1c3b0
--- /dev/null
+++ b/public/demo/logo-harbor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-northwind.svg b/public/demo/logo-northwind.svg
new file mode 100644
index 0000000..b48e8c6
--- /dev/null
+++ b/public/demo/logo-northwind.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-orbit.svg b/public/demo/logo-orbit.svg
new file mode 100644
index 0000000..8c1081a
--- /dev/null
+++ b/public/demo/logo-orbit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/logo-summit.svg b/public/demo/logo-summit.svg
new file mode 100644
index 0000000..6bdb841
--- /dev/null
+++ b/public/demo/logo-summit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/demo/slide-box-cards.json b/public/demo/slide-box-cards.json
index 1f8ac00..97583bf 100644
--- a/public/demo/slide-box-cards.json
+++ b/public/demo/slide-box-cards.json
@@ -3,6 +3,8 @@
{ "icon": "mug-hot", "title": "Coffee", "text": "Roasted on Tuesdays, sold by Friday.", "backText": "Two house blends and a single origin that changes each month. Ask at the counter for a taste of whatever is on the grinder.", "cta": { "text": "See this month’s beans", "href": "#slide-box-demo" }, "direction": "up" },
{ "icon": "truck-fast", "title": "Delivery", "text": "Beans at your door in two days.", "backText": "Order by noon and it ships the same day, in a bag that keeps the beans fresh for a month.", "cta": { "text": "How delivery works", "href": "#slide-box-demo" }, "direction": "left" },
{ "image": "promo.svg", "imageAlt": "", "title": "Wholesale", "text": "For cafés, offices and restaurants.", "backText": "Weekly deliveries, a grinder on loan and a morning of barista training for your staff.", "cta": { "text": "Ask for a price list", "href": "#slide-box-demo" }, "direction": "down" },
- { "icon": "calendar-check", "title": "Tasting mornings", "text": "Tap or click: this one ignores hover.", "backText": "The first Saturday of each month, 9 to 11. Six coffees, one table, no booking needed.", "cta": { "text": "Next dates", "href": "#slide-box-demo" }, "direction": "right", "trigger": "click" }
+ { "icon": "calendar-check", "title": "Tasting mornings", "text": "Tap or click: this one ignores hover.", "backText": "The first Saturday of each month, 9 to 11. Six coffees, one table, no booking needed.", "cta": { "text": "Next dates", "href": "#slide-box-demo" }, "direction": "right", "trigger": "click" },
+ { "image": "gallery-ridge.svg", "imageAlt": "", "title": "Where it grows", "text": "The photo is the card.", "backText": "Our single origins come from three farms on the same ridge, picked by hand and dried on raised beds.", "cta": { "text": "Meet the farms", "href": "#slide-box-demo" }, "direction": "up", "variant": "image" },
+ { "image": "gallery-coast.svg", "imageAlt": "", "title": "The roastery", "text": "A round card.", "backText": "Visit on Fridays and watch a batch go from green to brown.", "cta": { "text": "Plan a visit", "href": "#slide-box-demo" }, "direction": "up", "variant": "image", "shape": "circle" }
]
}
diff --git a/scripts/check-catalog.mjs b/scripts/check-catalog.mjs
index d9cf5d3..4e423f2 100644
--- a/scripts/check-catalog.mjs
+++ b/scripts/check-catalog.mjs
@@ -154,6 +154,7 @@ const NEEDS_INPUT = [
'flip-box', 'hotspot', 'info-circle', 'info-list', 'map', 'modal', 'news-ticker',
'responsive-table', 'slide-box', 'social-grid', 'tabcordion', 'tabs', 'testimonial-carousel',
'video-background', 'video-gallery', 'video-player', 'cart', 'product-list', 'product-page',
+ 'counter', 'logo-grid', 'photo-gallery', 'call-to-action',
];
const plainText = (v) => typeof v === 'string' && v.trim() === v && v.length > 0;
for (const e of catalog) {
diff --git a/scripts/check-categories.mjs b/scripts/check-categories.mjs
index ceda33d..670bcb1 100644
--- a/scripts/check-categories.mjs
+++ b/scripts/check-categories.mjs
@@ -61,6 +61,7 @@ const GOLDEN = {
'business-hours': 'content',
map: 'content',
'responsive-table': 'content',
+ counter: 'content',
'before-after': 'media',
'video-player': 'media',
@@ -69,6 +70,8 @@ const GOLDEN = {
'card-slider': 'media',
'testimonial-carousel': 'media',
icon: 'media',
+ 'logo-grid': 'media',
+ 'photo-gallery': 'media',
'image-effects': 'media',
countdown: 'conversion',
@@ -80,6 +83,7 @@ const GOLDEN = {
cart: 'conversion',
'product-list': 'conversion',
'product-page': 'conversion',
+ 'call-to-action': 'conversion',
notice: 'feedback',
toast: 'feedback',
@@ -92,6 +96,7 @@ const GOLDEN = {
tags: 'controls',
'link-effects': 'controls',
'cookie-consent': 'controls',
+ separator: 'controls',
};
const MIN_PER_CATEGORY = 3;
diff --git a/src/components/demos/CallToActionDemo.astro b/src/components/demos/CallToActionDemo.astro
new file mode 100644
index 0000000..8c1b8f2
--- /dev/null
+++ b/src/components/demos/CallToActionDemo.astro
@@ -0,0 +1,38 @@
+---
+/**
+ * Demo for `call-to-action`. The website renders this too (see README, "Consumed by
+ * superherotech.ai"), so every host token has a fallback. It uses no assets.
+ *
+ * Three bands: stacked and centred on the accent (UABB's Call to Action as latlonguas.com's
+ * home page has it), inline on dark with a phone link in the text (Beaver Builder's "cta" on
+ * its DC-FRZ page), and a light one with two buttons. Only the first is a region landmark.
+ */
+import CallToAction from '../../library/call-to-action/CallToAction.astro';
+---
+
+
+
+
Read the questions people often ask below, or call us on (555) 555-0123.
+
+
+
+
diff --git a/src/components/demos/CounterDemo.astro b/src/components/demos/CounterDemo.astro
new file mode 100644
index 0000000..7235db2
--- /dev/null
+++ b/src/components/demos/CounterDemo.astro
@@ -0,0 +1,61 @@
+---
+/**
+ * Demo for `counter`. The website renders this too (see README, "Consumed by
+ * superherotech.ai"), so every host token has a fallback. It uses no assets.
+ *
+ * Three rows: rings (the UABB "circle" look latlonguas.com uses), plain figures with a prefix
+ * and a suffix, and bars against a stated maximum. Each counts once as it scrolls into view;
+ * under reduced motion the final figures simply show.
+ */
+import Counter from '../../library/counter/Counter.astro';
+---
+
+
+
This year, so far
+
+
+
+
The workshop in numbers
+
+
+
+
Spring course places
+
+
+
+
diff --git a/src/components/demos/InfoListDemo.astro b/src/components/demos/InfoListDemo.astro
index c6d2b87..2967bca 100644
--- a/src/components/demos/InfoListDemo.astro
+++ b/src/components/demos/InfoListDemo.astro
@@ -4,8 +4,9 @@
* superherotech.ai"), so every host token has a fallback. It uses no assets; the icons are
* inline SVG.
*
- * Three lists: stacked with icons (two items linked), inline four-across numbered steps with
- * the connector ("how it works"), and a stacked numbered timeline with the connector.
+ * Four lists: stacked with icons (two items linked), inline four-across numbered steps with
+ * the connector ("how it works"), a stacked numbered timeline with the connector, and a row of
+ * boxed, centred info boxes (UABB's and PowerPack's Info Box), one of them linked.
*/
import InfoList from '../../library/info-list/InfoList.astro';
@@ -48,6 +49,11 @@ const history = [
{ meta: '2021', title: 'A team of six', text: 'Two crews, a designer and an office that answers the phone.' },
{ meta: '2025', title: 'Water-wise gardens', text: 'Drip irrigation and drought-tolerant planting become a service of their own.' },
];
+const boxes = [
+ { title: 'Free site visit', text: 'We walk the garden with you and listen before we draw anything.', icon: services[0].icon },
+ { title: 'Fixed written quote', text: 'One price, in writing, within two working days of the visit.', icon: services[1].icon, href: '#quote' },
+ { title: 'Two-year guarantee', text: 'Any plant we put in that fails in its first two years, we replace.', icon: services[3].icon },
+];
---
@@ -64,6 +70,11 @@ const history = [
Our story
+
+
+
Why people choose us
+
+
diff --git a/src/components/demos/PhotoGalleryDemo.astro b/src/components/demos/PhotoGalleryDemo.astro
new file mode 100644
index 0000000..52f2c38
--- /dev/null
+++ b/src/components/demos/PhotoGalleryDemo.astro
@@ -0,0 +1,47 @@
+---
+/**
+ * Demo for `photo-gallery`. The website renders this too (see README, "Consumed by
+ * superherotech.ai"), so assets go through `asset()` and every host token has a fallback.
+ *
+ * Eight aerial-style illustrations in public/demo/gallery-*.svg, in the four categories
+ * latlonguas.com's gallery filters by. Press a photo for the lightbox: ← → or the buttons
+ * step through what the filter shows.
+ */
+import PhotoGallery from '../../library/photo-gallery/PhotoGallery.astro';
+
+interface Props {
+ /** Where public/demo/ is served. The gallery uses /demo/; the website passes /elements-demo/. */
+ assetBase?: string;
+}
+const { assetBase = '/demo/' } = Astro.props;
+const asset = (name: string) => assetBase + name;
+const p = (file: string, alt: string, category: string, caption?: string) => ({
+ src: asset(file),
+ alt,
+ category,
+ caption,
+ width: 1200,
+ height: 800,
+});
+const photos = [
+ p('gallery-roofs.svg', 'Rooftops of a town centre from above, with the main road crossing it', 'Buildings', 'Roof survey, town centre'),
+ p('gallery-tower.svg', 'A telecom mast seen from directly above, its three guy lines spread out', 'Structures'),
+ p('gallery-pylons.svg', 'Power lines crossing green fields between three pylons', 'Power lines', 'Line inspection, after the storm'),
+ p('gallery-coast.svg', 'A sandy coastline with surf breaking along it', 'Scenic'),
+ p('gallery-campus.svg', 'A business park of low buildings and car parks', 'Buildings'),
+ p('gallery-quarry.svg', 'The terraced pit of a quarry, a haul road along its edge', 'Structures', 'Stockpile volumes, quarterly'),
+ p('gallery-substation.svg', 'Lines running into a rural substation', 'Power lines'),
+ p('gallery-ridge.svg', 'Layered mountain ridges under a pale sun', 'Scenic'),
+];
+---
+
+
+
+
diff --git a/src/components/demos/SeparatorDemo.astro b/src/components/demos/SeparatorDemo.astro
new file mode 100644
index 0000000..576eaba
--- /dev/null
+++ b/src/components/demos/SeparatorDemo.astro
@@ -0,0 +1,42 @@
+---
+/**
+ * Demo for `separator`. The website renders this too (see README, "Consumed by
+ * superherotech.ai"), so every host token has a fallback. It uses no assets; the icon is
+ * inline SVG.
+ *
+ * Six separators: a plain line, a short accent bar that grows into view (the slide-in the old
+ * Beaver Builder pages used), dashed with a word, a heading between two lines, dotted with an
+ * icon, and a double line as a real thematic break.
+ */
+import Separator from '../../library/separator/Separator.astro';
+
+const star = ``;
+---
+
+
A plain line
+
+
A short accent bar, growing into view
+
+
Dashed, with a word
+
+
A heading between two lines
+
+
Dotted, with an icon, aligned to the start
+
+
Double, as a real thematic break
+
+
+
diff --git a/src/components/demos/SlideBoxDemo.astro b/src/components/demos/SlideBoxDemo.astro
index 1e4451a..3e808f1 100644
--- a/src/components/demos/SlideBoxDemo.astro
+++ b/src/components/demos/SlideBoxDemo.astro
@@ -5,7 +5,9 @@
*
* Four cards, one per `direction`: the back rises from the bottom (up, the default), comes in
* from the right (left), drops from the top (down, on an image card) and comes in from the left
- * (right, on a card that ignores hover). The cards live in public/demo/slide-box-cards.json,
+ * (right, on a card that ignores hover); then an image card, where the photo is the card
+ * (variant="image", UABB's Interactive Banner), and a round one (shape="circle", UABB's iHover).
+ * The cards live in public/demo/slide-box-cards.json,
* which scripts/check-reveal-cards.mjs reads to pin the built page against.
*
* The icons are Font Awesome Free (CC BY 4.0), pasted with their attribution comment exactly as
@@ -37,6 +39,8 @@ type Card = {
cta: { text: string; href: string };
direction: 'up' | 'down' | 'left' | 'right';
trigger?: 'both' | 'click';
+ variant?: 'card' | 'image';
+ shape?: 'rect' | 'circle';
};
const cards = (fixture.cards as Card[]).map(({ icon, image, ...c }) => ({
...c,
diff --git a/src/components/demos/index.ts b/src/components/demos/index.ts
index ae661e0..3ba1504 100644
--- a/src/components/demos/index.ts
+++ b/src/components/demos/index.ts
@@ -61,6 +61,11 @@ import StickerDemo from './StickerDemo.astro';
import CartDemo from './CartDemo.astro';
import ProductListDemo from './ProductListDemo.astro';
import ProductPageDemo from './ProductPageDemo.astro';
+import CounterDemo from './CounterDemo.astro';
+import LogoGridDemo from './LogoGridDemo.astro';
+import PhotoGalleryDemo from './PhotoGalleryDemo.astro';
+import SeparatorDemo from './SeparatorDemo.astro';
+import CallToActionDemo from './CallToActionDemo.astro';
import ImageEffectsDemo from './ImageEffectsDemo.astro';
// Astro components have no exported public type; this is the shape both callers need.
@@ -114,5 +119,10 @@ export const demos: Record = {
cart: CartDemo,
'product-list': ProductListDemo,
'product-page': ProductPageDemo,
+ counter: CounterDemo,
+ 'logo-grid': LogoGridDemo,
+ 'photo-gallery': PhotoGalleryDemo,
+ separator: SeparatorDemo,
+ 'call-to-action': CallToActionDemo,
'image-effects': ImageEffectsDemo,
};
diff --git a/src/data/catalog.ts b/src/data/catalog.ts
index f4a6ab5..e83525f 100644
--- a/src/data/catalog.ts
+++ b/src/data/catalog.ts
@@ -122,6 +122,13 @@ export interface Element {
* licensed third-party material (so far: `icon`, on Font Awesome) carry one. */
license?: string;
usedOn: { site: string; where: string }[];
+ /**
+ * A live page that still runs the WordPress / page-builder module this element replaces, to
+ * look at before rebuilding one: `module` is the module as the page names it (the
+ * `fl-module-` class on a Beaver Builder page). These are clients' production sites:
+ * read them, never change anything there.
+ */
+ reference?: { module: string; url: string; note?: string }[];
/** Path of the component in this repo — the file to copy into a client build. */
/** Where the file is really maintained, when this repo only holds a synced copy. */
source?: { repo: string; sync: string };
@@ -287,7 +294,7 @@ export const catalog: Element[] = [
id: 'video-background',
category: 'motion',
name: 'Video background',
- aka: ['video hero', 'background video', 'Elementor video background', 'autoplay muted loop video', 'Beaver Builder video row', 'hero video'],
+ aka: ['video hero', 'background video', 'Elementor video background', 'autoplay muted loop video', 'Beaver Builder video row', 'hero video', 'fl-bg-video'],
summary: 'A muted, looping video behind a hero, over a poster, with an optional tint and a visible pause button. Only the poster loads on small screens, under reduced motion and without JavaScript.',
pitch: 'Open your home page with moving footage of your work, without making visitors wait for it or wrestle with it.',
// 320/mo, difficulty 21 (SE Ranking US, 2026-09-23).
@@ -339,6 +346,9 @@ export const catalog: Element[] = [
`,
usedOn: [{ site: 'superherotech.ai', where: '/elements/video-background/ (demo)' }],
+ reference: [
+ { module: 'Beaver Builder row video (fl-bg-video)', url: 'https://latlonguas.com/', note: 'The hero on every page. The footage plays from Cloudflare Stream through video.latlonguas.com, the superhero-video Worker.' },
+ ],
file: 'src/library/video-background/VideoBackground.astro',
added: '2026-09-23',
},
@@ -754,7 +764,7 @@ const feed = social as SocialFeed;
id: 'accordion',
category: 'navigation',
name: 'Accordion / FAQ',
- aka: ['PowerPack FAQ Module', 'PowerPack Advanced Accordions', 'UABB Advanced Accordion', 'Elementor Accordion', 'FAQ accordion', 'FAQ schema', 'toggle', 'collapsible'],
+ aka: ['PowerPack FAQ Module', 'PowerPack Advanced Accordions', 'UABB Advanced Accordion', 'Elementor Accordion', 'FAQ accordion', 'FAQ schema', 'toggle', 'collapsible', 'pp-advanced-accordion'],
summary:
'Questions (or any titles) that open to show their answer, on native /: keyboard, screen readers and find-in-page work with no script. One open at a time or several, numbered, chevron or plus, deep-linkable by #id. With jsonLd on, the same items become the page’s FAQPage structured data, so the FAQ and the accordion are one element.',
pitch: 'FAQ page design that gets people their answer fast: each question opens with a tap, every answer is findable with Ctrl+F, and search engines and AI assistants can read them as questions and answers.',
@@ -824,6 +834,9 @@ const faqs: AccordionItem[] = [
{ site: 'jwalktours.com', where: '/faqs/ and the tour pages: its own FaqList.astro (, FAQPage JSON-LD from the same array); moves to this element on its next FAQ request' },
{ site: 'stbeautybar.com', where: '/about-us/ FAQ: its own Faqs.astro (exclusive, first open, rotating icon, FAQPage JSON-LD); moves to this element on its next FAQ request' },
],
+ reference: [
+ { module: 'pp-advanced-accordion', url: 'https://latlonguas.com/dc-frz/', note: 'The FRZ questions at the foot of the page.' },
+ ],
file: 'src/library/accordion/Accordion.astro',
added: '2026-09-23',
},
@@ -958,14 +971,14 @@ const faqs: AccordionItem[] = [
id: 'info-list',
category: 'content',
name: 'Info list',
- aka: ['PowerPack Info List', 'UABB Info List', 'Elementor Icon List', 'icon list', 'steps list', 'process steps', 'vertical timeline', 'feature list'],
+ aka: ['PowerPack Info List', 'UABB Info List', 'Elementor Icon List', 'icon list', 'steps list', 'process steps', 'vertical timeline', 'feature list', 'UABB Info Box', 'PowerPack Info Box', 'info box', 'info-box'],
summary:
'Items with a marker (icon, image, number or dot), a title, a line of text and an optional link, stacked or inline in columns. With the connector the markers are joined by a line: a steps list or a vertical timeline. HTML and CSS only; there is no script.',
pitch: 'Lay out your services, how you work step by step, or your story year by year, each with an icon or a number, in a list that reads on any screen.',
// vertical timeline 500/mo, difficulty 21 (rising); icon list 660/45; timeline design website
// 170/25 (SE Ranking US, 2026-09-23).
search: { query: 'vertical timeline', alsoRanks: ['icon list', 'timeline design website'] },
- replaces: ['PowerPack “Info List” module (Beaver Builder)', 'UABB “Info List”', 'Elementor Icon List widget', 'hand-written icon lists'],
+ replaces: ['PowerPack “Info List” module (Beaver Builder)', 'UABB “Info List”', 'Elementor Icon List widget', 'hand-written icon lists', 'UABB and PowerPack “Info Box” modules (boxed and centred)'],
goodFor: 'Services, “how it works”, process steps, milestones and feature lists.',
notFor: 'Navigation (that is a menu) and long prose (write paragraphs).',
asks: [
@@ -980,6 +993,8 @@ const faqs: AccordionItem[] = [
{ name: 'markerPosition', type: '“left” | “top”', default: 'left for stack, top for inline', note: 'Top applies where inline has more than one column.' },
{ name: 'headingLevel', type: '2 | 3 | 4 | 5', default: '3', note: 'Level of each title.' },
{ name: 'size', type: '“sm” | “md” | “lg”', default: '“md”', note: 'Marker size: 2.25, 3 or 4rem. A dot is 1rem.' },
+ { name: 'boxed', type: 'boolean', default: 'false', note: 'Each item a card: the UABB / PowerPack Info Box. With `href` the whole card is the link. No connector.' },
+ { name: 'align', type: '“start” | “center”', default: '“start”', note: 'center: marker on top and everything centred, at every width (the usual info-box look).' },
{ name: 'class', type: 'string', note: 'Class on the list, for the host to theme it.' },
],
theming: [
@@ -993,6 +1008,11 @@ const faqs: AccordionItem[] = [
{ name: '--il-title / --il-text / --il-meta', fallback: 'inherit', note: 'Text colors.' },
{ name: '--il-gap / --il-col-gap', fallback: '1.75rem / 1.5rem', note: 'Space between rows / columns. The connector spans the row gap.' },
{ name: '--il-focus', fallback: 'currentColor', note: 'Focus ring around a linked item.' },
+ { name: '--il-box-bg', fallback: '#fff', note: '`boxed`: card background.' },
+ { name: '--il-box-border', fallback: '1px solid rgb(0 0 0 / 0.1)', note: '`boxed`: card border (a whole border shorthand).' },
+ { name: '--il-box-radius', fallback: '14px', note: '`boxed`: card corners, and the linked card’s focus ring.' },
+ { name: '--il-box-pad', fallback: '1.5rem', note: '`boxed`: inside each card.' },
+ { name: '--il-box-shadow', fallback: 'none', note: '`boxed`: card box-shadow.' },
],
a11y: [
'A real list: when the order matters (connector or numbers),
otherwise, with role="list" so Safari keeps announcing it with list-style removed.',
@@ -1014,6 +1034,9 @@ const faqs: AccordionItem[] = [
({ meta: h.year, title: h.title, text: h.text }))} marker="dot" connector />
`,
usedOn: [{ site: 'superherotech.ai', where: '/elements/info-list/ (demo)' }],
+ reference: [
+ { module: 'info-box', url: 'https://latlonguas.com/', note: 'The two-up “From the strictly functional…” boxes: boxed, centred, no icon.' },
+ ],
file: 'src/library/info-list/InfoList.astro',
added: '2026-09-23',
},
@@ -1021,7 +1044,7 @@ const faqs: AccordionItem[] = [
id: 'video-player',
category: 'media',
name: 'Video player',
- aka: ['PowerPack Video', 'UABB Video', 'Elementor Video', 'YouTube embed', 'Vimeo embed', 'video lightbox', 'lite YouTube embed'],
+ aka: ['PowerPack Video', 'UABB Video', 'Elementor Video', 'YouTube embed', 'Vimeo embed', 'video lightbox', 'lite YouTube embed', 'pp-video'],
summary:
'A YouTube, Vimeo or self-hosted video behind your own poster and play button, in place or in a lightbox. Nothing is requested from YouTube or Vimeo until the button is pressed; then a privacy-enhanced iframe is created and focus moves into it.',
pitch: 'Your video, your thumbnail, and nothing loads from YouTube until someone presses play.',
@@ -1091,6 +1114,9 @@ const faqs: AccordionItem[] = [
/>
`,
usedOn: [{ site: 'superherotech.ai', where: '/elements/video-player/ (demo)' }],
+ reference: [
+ { module: 'pp-video', url: 'https://latlonguas.com/services/', note: 'Two videos on the services page.' },
+ ],
file: 'src/library/video-player/VideoPlayer.astro',
added: '2026-09-23',
},
@@ -1211,7 +1237,7 @@ import { testimonials } from '../data/testimonials'; // [{ quote, name, meta }
id: 'animated-text',
category: 'motion',
name: 'Animated text',
- aka: ['animated headline', 'Elementor Animated Headline', 'text rotator', 'word rotator', 'rotating text', 'typewriter effect', 'typing animation', 'Typed.js', 'fancy text', 'word reveal', 'highlighted headline'],
+ aka: ['animated headline', 'Elementor Animated Headline', 'text rotator', 'word rotator', 'rotating text', 'typewriter effect', 'typing animation', 'Typed.js', 'fancy text', 'word reveal', 'highlighted headline', 'UABB Fancy Text', 'fancy-text'],
summary:
'One line of text in one of six effects: build (word by word), typewriter, rotate (one slot turns over), distil (words fall away to a shorter phrase), highlight (a marker or underline sweeps), strike (a word is struck and replaced). Speed, cadence, hold, repeat, entrance and type are set per use. Nothing reflows, a screen reader hears the sentence once, and it rests whole under reduced motion or with no script.',
pitch: 'Let your most important line arrive the way you would say it out loud, in your own type and at your own pace.',
@@ -1293,6 +1319,9 @@ import AnimatedText from '../components/AnimatedText.astro';
{ site: 'nicolelawton.com', where: 'Resources, the closing tagline "Cultivate Empower Connect": build, word by word, gap 600, once. A copy of this file; her phoenix between the words is drawn by the page, on .at__piece + .at__piece::before (pre-launch on nicolelawton.gohero.us)' },
{ site: 'superherotech.ai', where: '/elements/animated-text/ (demo): all six effects' },
],
+ reference: [
+ { module: 'fancy-text', url: 'https://latlonguas.com/', note: '“Where above meets beyond!” under the hero; typed text on /services/ and /our-story/ too.' },
+ ],
file: 'src/library/animated-text/AnimatedText.astro',
added: '2026-09-23',
},
@@ -1736,7 +1765,7 @@ import { analytics } from '../data/site';
id: 'slide-box',
category: 'content',
name: 'Slide box',
- aka: ['UABB Slide Box', 'slide box', 'reveal card', 'sliding card', 'hover reveal box', 'info box with slide-up detail', 'card overlay'],
+ aka: ['UABB Slide Box', 'slide box', 'reveal card', 'sliding card', 'hover reveal box', 'info box with slide-up detail', 'card overlay', 'UABB Interactive Banner', 'interactive-banner-1', 'UABB iHover', 'ihover', 'image hover effects'],
summary:
'A card whose back panel slides in over the front from a chosen direction (up, down, left, right): on hover where there is a mouse, and on click, tap, Enter or Space everywhere, through a real button with aria-expanded. The front is a teaser (icon or image, title, a line), the back its detail (text and a link). Both panels share one height; the one out of view is inert.',
pitch: 'A card that shows the headline first and slides up the detail when someone wants it, on a phone as well as with a mouse.',
@@ -1744,7 +1773,7 @@ import { analytics } from '../data/site';
// hover effects 30/12; card overlay 50/10. "slide box" itself is 1,300/55, but its results
// are microscope-slide storage boxes and toolbox drawer slides, not websites, so it stays an aka.
search: { query: 'reveal card', alsoRanks: ['sliding card', 'card hover effects', 'card overlay'] },
- replaces: ['UABB “Slide Box” module (Beaver Builder), overlay style', 'hover-reveal info boxes in Elementor and Divi', 'hand-rolled CSS slide-up card overlays'],
+ replaces: ['UABB “Slide Box” module (Beaver Builder), overlay style', 'hover-reveal info boxes in Elementor and Divi', 'hand-rolled CSS slide-up card overlays', 'UABB “Interactive Banner 1 / 2” (variant="image")', 'UABB “iHover” (variant="image" shape="circle")'],
goodFor:
'A grid of teasers whose detail is worth one more gesture: services that each need three lines of explanation and a link, job openings (title on the front, what the job is and Apply on the back), products with ingredients or specifications, team members with a short bio.',
notFor:
@@ -1766,6 +1795,8 @@ import { analytics } from '../data/site';
{ name: 'trigger', type: "'both' | 'click'", default: "'both'", note: 'Whether hover ALSO slides it in. Click, tap, Enter and Space always do; a hover-only card would lock out touch screens and keyboards.' },
{ name: 'duration', type: 'number (ms)', note: 'The slide. Overrides `--slb-duration` (450ms).' },
{ name: 'headingLevel', type: '2 | 3 | 4 | 5 | 6', default: '3', note: 'Level of the headings; match the page outline.' },
+ { name: 'variant', type: "'card' | 'image'", default: "'card'", note: 'image: the photo fills the card, the title sits on a scrim at its foot and the back is a tint that slides in over the photo (UABB Interactive Banner). Needs `image`.' },
+ { name: 'shape', type: "'rect' | 'circle'", default: "'rect'", note: 'circle: a round card (UABB iHover), once the script runs; without it the panels stack as rounded boxes. Keep the back to about 20 words.' },
{ name: 'label', type: 'string', note: 'Accessible name of the card’s button. Default: `title`, else the front’s first heading.' },
{ name: 'class', type: 'string', note: 'Class on the root, for theming one card.' },
{ name: 'slot front / slot back', type: 'slot', note: 'Any HTML for a panel; wins over the props for that panel. Keep links off the front: the whole front is the button.' },
@@ -1791,6 +1822,10 @@ import { analytics } from '../data/site';
{ name: '--slb-easing', fallback: 'cubic-bezier(0.2, 0.7, 0.2, 1)', note: 'Timing of the slide.' },
{ name: '--slb-fade', fallback: '200ms', note: 'The crossfade under reduced motion.' },
{ name: '--slb-focus', fallback: 'var(--slb-accent)', note: 'Focus ring round the card.' },
+ { name: '--slb-image-fg', fallback: '#fff', note: 'variant="image": text over the photo and on its tinted back.' },
+ { name: '--slb-scrim', fallback: 'linear-gradient(to top, rgb(20 24 40 / 0.85), rgb(20 24 40 / 0.55) 60%, transparent)', note: 'variant="image": behind the front’s title, for 4.5:1 over the photo.' },
+ { name: '--slb-image-back-bg', fallback: 'rgb(20 24 40 / 0.86)', note: 'variant="image": the tint that slides in over the photo.' },
+ { name: '--slb-image-height', fallback: '16rem', note: 'variant="image": the card’s floor.' },
],
a11y: [
'The card is a real