diff --git a/public/demo/hotspot-points.json b/public/demo/hotspot-points.json
index 4ad770e..61882ba 100644
--- a/public/demo/hotspot-points.json
+++ b/public/demo/hotspot-points.json
@@ -18,7 +18,7 @@
"x": 50,
"y": 62,
"title": "Three-seat sofa",
- "text": "Removable, washable covers in twelve colours. Delivered and assembled in the room."
+ "text": "Removable, washable covers in twelve colors. Delivered and assembled in the room."
},
{
"x": 87,
diff --git a/src/components/demos/AccordionDemo.astro b/src/components/demos/AccordionDemo.astro
index 569f5dc..771adf2 100644
--- a/src/components/demos/AccordionDemo.astro
+++ b/src/components/demos/AccordionDemo.astro
@@ -12,7 +12,7 @@ const faqs = [
{
id: 'custom-cakes',
title: 'Do you take custom cake orders?',
- body: '
Yes. Order at least five days ahead for a celebration cake, and two weeks ahead for a wedding. Tell us the size, the flavour and any lettering, and we confirm the price before we bake.
',
+ body: 'Yes. Order at least five days ahead for a celebration cake, and two weeks ahead for a wedding. Tell us the size, the flavor and any lettering, and we confirm the price before we bake.
',
},
{
id: 'order-ahead',
diff --git a/src/components/demos/BeforeAfterDemo.astro b/src/components/demos/BeforeAfterDemo.astro
index 66cf4ed..d6f2dc3 100644
--- a/src/components/demos/BeforeAfterDemo.astro
+++ b/src/components/demos/BeforeAfterDemo.astro
@@ -15,7 +15,7 @@ const asset = (name: string) => assetBase + name;
A 12 oz stoneware mug, thrown by hand and glazed in a speckled oat colour. Each one is a little different.',
+ body: 'A 12 oz stoneware mug, thrown by hand and glazed in a speckled oat color. Each one is a little different.
',
},
{
id: 'mug-size',
diff --git a/src/data/catalog.ts b/src/data/catalog.ts
index 16c1da3..ee1ef73 100644
--- a/src/data/catalog.ts
+++ b/src/data/catalog.ts
@@ -202,7 +202,7 @@ export const catalog: Element[] = [
{ name: 'minWidth', type: 'number', default: '700', note: 'Draw nothing below this viewport width. 0 draws everywhere. Tracked live, not decided once.' },
{ name: 'class', type: 'string', note: 'Class for the host to position and stack it with.' },
],
- theming: [{ name: '--pf-color', fallback: '#fff', note: 'Dot and line colour. Also settable as plain `color` on the canvas.' }],
+ theming: [{ name: '--pf-color', fallback: '#fff', note: 'Dot and line color. Also settable as plain `color` on the canvas.' }],
a11y: [
'Decorative: aria-hidden, no pointer events.',
'prefers-reduced-motion: one still frame is drawn, so the look survives without the movement.',
@@ -307,10 +307,10 @@ export const catalog: Element[] = [
{ name: 'class', type: 'string', note: 'Class on the wrapper, for the host to theme and size it.' },
],
theming: [
- { name: '--vb-bg', fallback: '#111', note: 'Colour behind the poster while it loads.' },
- { name: '--vb-overlay', fallback: '#000', note: 'Tint colour.' },
+ { name: '--vb-bg', fallback: '#111', note: 'Color behind the poster while it loads.' },
+ { name: '--vb-overlay', fallback: '#000', note: 'Tint color.' },
{ name: '--vb-overlay-opacity', fallback: '0.4', note: 'Tint strength; the `overlay` prop overrides it.' },
- { name: '--vb-fg', fallback: '#fff', note: 'Content text colour.' },
+ { name: '--vb-fg', fallback: '#fff', note: 'Content text color.' },
{ name: '--vb-control-bg', fallback: 'rgb(0 0 0 / 0.55)', note: 'Pause button fill.' },
{ name: '--vb-control-fg', fallback: '#fff', note: 'Pause button icon and ring.' },
{ name: '--vb-focus', fallback: '#fff', note: 'Pause button focus ring.' },
@@ -368,10 +368,10 @@ export const catalog: Element[] = [
{ name: 'class', type: 'string', note: 'Class on the band, for the host to theme it.' },
],
theming: [
- { name: '--pb-bg', fallback: '#222', note: 'Colour behind the image while it loads.' },
- { name: '--pb-overlay', fallback: '#000', note: 'Tint colour.' },
+ { name: '--pb-bg', fallback: '#222', note: 'Color behind the image while it loads.' },
+ { name: '--pb-overlay', fallback: '#000', note: 'Tint color.' },
{ name: '--pb-overlay-opacity', fallback: '0.35', note: 'Tint strength; the `overlay` prop overrides it.' },
- { name: '--pb-fg', fallback: '#fff', note: 'Content text colour.' },
+ { name: '--pb-fg', fallback: '#fff', note: 'Content text color.' },
{ name: '--pb-height', fallback: '60vh', note: 'Minimum height; the `minHeight` prop overrides it.' },
],
a11y: [
@@ -476,12 +476,12 @@ export const catalog: Element[] = [
{ name: '--bh-badge-bg', fallback: 'transparent', note: 'Fill behind the pill.' },
{ name: '--bh-today-bg', fallback: 'rgb(0 0 0 / 0.05)', note: 'Today’s row in the table.' },
{ name: '--bh-line', fallback: 'rgb(0 0 0 / 0.15)', note: 'Table rules.' },
- { name: '--bh-text', fallback: 'inherit', note: 'Text colour.' },
+ { name: '--bh-text', fallback: 'inherit', note: 'Text color.' },
],
a11y: [
'The week is a real table with a caption naming the time zone, and scoped column and row headers. Today’s row says "(today)" and carries aria-current="date"; the highlight is on top of the word.',
'The sentence is in an aria-live="polite" region, rewritten only when its text changes, not every minute.',
- 'Open and closed are words; colour and the dot are on top. Times are elements.',
+ 'Open and closed are words; color and the dot are on top. Times are elements.',
'The static HTML never claims a state, so a cached page is never wrong; with JavaScript off the table and both slots show.',
'No motion, so nothing to reduce.',
],
@@ -539,13 +539,13 @@ import { hours } from '../data/hours'; // export const hours: BusinessHoursDat
{ name: 'class', type: 'string', note: 'Class on the strip, for the host to theme and place it.' },
],
theming: [
- { name: '--nt-accent', fallback: '#3b2fc9', note: 'The host’s accent: the label chip, hover colour and focus ring all follow it.' },
+ { name: '--nt-accent', fallback: '#3b2fc9', note: 'The host’s accent: the label chip, hover color and focus ring all follow it.' },
{ name: '--nt-bg', fallback: '#f1f2f6', note: 'Strip background.' },
{ name: '--nt-fg', fallback: '#1b1c22', note: 'Strip text and button icons.' },
{ name: '--nt-label-bg', fallback: 'var(--nt-accent)', note: 'Label chip fill, if it should differ from the accent.' },
{ name: '--nt-label-fg', fallback: '#fff', note: 'Label chip text.' },
- { name: '--nt-link', fallback: 'inherit', note: 'Headline colour.' },
- { name: '--nt-link-hover', fallback: 'var(--nt-accent)', note: 'Headline colour on hover and focus.' },
+ { name: '--nt-link', fallback: 'inherit', note: 'Headline color.' },
+ { name: '--nt-link-hover', fallback: 'var(--nt-accent)', note: 'Headline color on hover and focus.' },
{ name: '--nt-muted', fallback: '#585b6b', note: 'Meta text and the marquee’s dots. Keep 4.5:1 on --nt-bg.' },
{ name: '--nt-focus', fallback: 'var(--nt-accent)', note: 'Focus ring.' },
{ name: '--nt-control-line', fallback: 'rgb(0 0 0 / 0.25)', note: 'Button ring.' },
@@ -723,7 +723,7 @@ const posts = (await getCollection('blog')).sort((a, b) => +b.data.date - +a.dat
'An empty alt falls back to the caption’s first sentence, then to "Instagram post from "; a name that would repeat gets a number. No tile is nameless.',
'Hover captions also show on keyboard focus, and are aria-hidden: the text lives in the alt, never only in the overlay. Captions below are ordinary text after the link.',
'Every link opens Instagram in a new tab and says so once, through aria-describedby, rather than in each name.',
- 'The grid is a list labelled by the header line. The caption fade is instant under prefers-reduced-motion. No JavaScript.',
+ 'The grid is a list labeled by the header line. The caption fade is instant under prefers-reduced-motion. No JavaScript.',
],
usage: `// package.json: the site fetches the feed before every build
"prebuild": "node scripts/fetch-social.mjs"
@@ -778,7 +778,7 @@ const feed = social as SocialFeed;
{ name: 'items', type: 'AccordionItem[]', note: '`{ title, body?, slot?, id?, open? }`. `title` is plain text (the summary and the JSON-LD question). `body` is an HTML string; or name a slot in `slot` and pass `…
`. `id` is the deep-link anchor (default: the title, slugified).' },
{ name: 'exclusive', type: 'boolean', default: 'true', note: 'One open at a time. Uses the `name` attribute, so the browser closes the others itself.' },
{ name: 'openFirst', type: 'boolean', default: 'false', note: 'Open the first item on load.' },
- { name: 'numbered', type: 'boolean', default: 'false', note: '“1) 2) 3)” before each title, in the accent colour.' },
+ { name: 'numbered', type: 'boolean', default: 'false', note: '“1) 2) 3)” before each title, in the accent color.' },
{ name: 'icon', type: '“chevron” | “plus” | “none”', default: '“chevron”', note: 'Chevron turns over; plus becomes a minus.' },
{ name: 'boxed', type: 'boolean', default: 'false', note: 'Separate bordered boxes with `--ac-gap` between, instead of a ruled list.' },
{ name: 'headingLevel', type: '2 | 3 | 4', default: '3', note: 'Heading level of each title inside its summary, so the page outline stays unbroken.' },
@@ -867,7 +867,7 @@ const faqs: AccordionItem[] = [
],
a11y: [
'The track is a region with aria-roledescription="carousel" and the `label` as its name, and it is focusable, so ← → scroll it. Each card is role="group" with aria-roledescription="slide" and “3 of 8” in its name.',
- 'The cards’ own links are ordinary tab stops; focusing one scrolls its whole card into view. Arrows and dots are real buttons with names (“Previous”, “Page 2 of 3”); the current dot has aria-current and a longer shape, not only a colour. No aria-live: nothing is announced on its own.',
+ 'The cards’ own links are ordinary tab stops; focusing one scrolls its whole card into view. Arrows and dots are real buttons with names (“Previous”, “Page 2 of 3”); the current dot has aria-current and a longer shape, not only a color. No aria-live: nothing is announced on its own.',
'Swiping, trackpad and scroll-wheel are the browser’s own scrolling. Paging is smooth, and instant under prefers-reduced-motion.',
'Autoplay (off by default) has a visible pause button, WCAG 2.2.2, named for what it will do. It waits while hovered, while focus is inside, while the tab is hidden and while off screen, and a swipe restarts its countdown. Under prefers-reduced-motion (tracked live) it never starts and the button is not shown.',
'Without JavaScript the track scrolls by hand with its scrollbar visible; arrows, dots and the pause button are absent.',
@@ -984,12 +984,12 @@ const faqs: AccordionItem[] = [
theming: [
{ name: '--il-accent', fallback: '#3451b2', note: 'Default marker fill and dot ring. Map it to the host accent.' },
{ name: '--il-marker-bg', fallback: 'var(--il-accent)', note: 'Marker fill (for outline icons: a tint or transparent).' },
- { name: '--il-marker-fg', fallback: '#fff', note: 'Icon or number colour.' },
+ { name: '--il-marker-fg', fallback: '#fff', note: 'Icon or number color.' },
{ name: '--il-marker-size', fallback: 'by `size`', note: 'Marker box, any length.' },
{ name: '--il-marker-radius', fallback: '50%', note: 'Marker corners; 8px for rounded squares.' },
- { name: '--il-connector', fallback: 'rgb(0 0 0 / 0.18)', note: 'Connector colour.' },
+ { name: '--il-connector', fallback: 'rgb(0 0 0 / 0.18)', note: 'Connector color.' },
{ name: '--il-connector-width', fallback: '2px', note: 'Connector thickness.' },
- { name: '--il-title / --il-text / --il-meta', fallback: 'inherit', note: 'Text colours.' },
+ { 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.' },
],
@@ -1059,9 +1059,9 @@ const faqs: AccordionItem[] = [
{ name: '--vp-play-size', fallback: '4.5rem', note: 'Diameter of the circle; the icon is 45% of it.' },
{ name: '--vp-overlay', fallback: 'rgb(0 0 0 / 0.12)', note: 'Tint over the poster (keeps a white icon readable on a light poster).' },
{ name: '--vp-radius', fallback: '0', note: 'Corner radius of the frame.' },
- { name: '--vp-bg', fallback: '#000', note: 'Frame colour behind the poster, the video and the letterbox.' },
+ { name: '--vp-bg', fallback: '#000', note: 'Frame color behind the poster, the video and the letterbox.' },
{ name: '--vp-focus', fallback: '#fff', note: 'Focus ring on the play and close buttons (a dark halo sits outside it).' },
- { name: '--vp-caption', fallback: 'inherit', note: 'Caption text colour.' },
+ { name: '--vp-caption', fallback: 'inherit', note: 'Caption text color.' },
{ name: '--vp-backdrop', fallback: 'rgb(0 0 0 / 0.88)', note: 'Lightbox backdrop.' },
{ name: '--vp-close-bg', fallback: 'rgb(255 255 255 / 0.15)', note: 'Lightbox close button fill.' },
{ name: '--vp-close-fg', fallback: '#fff', note: 'Lightbox close icon.' },
@@ -1070,7 +1070,7 @@ const faqs: AccordionItem[] = [
'The play button is a real named “Play: ”, over the whole poster; Tab reaches it and Enter or Space plays. The poster image has its own alt text.',
'In place, focus moves into the player on play: the iframe (titled with the video’s title) or the . The keyboard then drives the player itself.',
'Self-hosted video keeps the browser’s native controls: keyboard operable, with a captions menu for `tracks`. A captions track is expected because a video with speech and no captions fails WCAG 1.2.2, and the build warns without one.',
- 'Lightbox: a native modal named with the title, so focus is trapped and Escape closes it; the close button is labelled and a backdrop click closes too. On open the video starts and focus goes to the close button, because a YouTube or Vimeo iframe keeps every key, Escape included; Tab moves on into the player. Closing removes the iframe or pauses the file, so sound never continues behind it, and returns focus to the play button. The page does not scroll underneath.',
+ 'Lightbox: a native modal named with the title, so focus is trapped and Escape closes it; the close button is labeled and a backdrop click closes too. On open the video starts and focus goes to the close button, because a YouTube or Vimeo iframe keeps every key, Escape included; Tab moves on into the player. Closing removes the iframe or pauses the file, so sound never continues behind it, and returns focus to the play button. The page does not scroll underneath.',
'Nothing plays until the visitor asks, so there is no autoplay to pause. prefers-reduced-motion only removes the play button’s hover growth.',
'Without JavaScript a file plays through its native controls, and YouTube, Vimeo and the lightbox offer a plain link to watch the video where it lives.',
],
@@ -1188,7 +1188,7 @@ import { testimonials } from '../data/testimonials'; // [{ quote, name, meta }
{ name: 'title', type: 'string', note: 'Optional native tooltip text (an SVG ), independent of `label`.' },
{ name: 'class', type: 'string', note: 'Class on the , for the host to size or theme it further.' },
],
- theming: [{ name: '--ic-color', fallback: 'currentColor', note: 'Icon colour. Most call sites need none of this: the icon already follows the surrounding text colour.' }],
+ theming: [{ name: '--ic-color', fallback: 'currentColor', note: 'Icon color. Most call sites need none of this: the icon already follows the surrounding text color.' }],
a11y: [
'Decorative by default: no `label` means `aria-hidden="true"`, because most icons sit beside text that already says what they mean.',
'With `label`, the icon gets `role="img"` and that exact text as its accessible name — set it whenever the icon is the only content conveying meaning, such as an icon-only button.',
@@ -1258,9 +1258,9 @@ import { testimonials } from '../data/testimonials'; // [{ quote, name, meta }
{ name: '--at-weight', fallback: 'inherit', note: 'Weight.' },
{ name: '--at-line-height', fallback: 'inherit', note: 'Line height.' },
{ name: '--at-tracking', fallback: 'inherit', note: 'Letter spacing.' },
- { name: '--at-ink', fallback: 'inherit', note: 'Text colour.' },
+ { name: '--at-ink', fallback: 'inherit', note: 'Text color.' },
{ name: '--at-align', fallback: 'inherit', note: 'Alignment.' },
- { name: '--at-slot-display', fallback: 'inline-grid', note: 'Rotate’s slot. `grid` gives it a line of its own (“We build” / “websites”), which a centred headline wants.' },
+ { name: '--at-slot-display', fallback: 'inline-grid', note: 'Rotate’s slot. `grid` gives it a line of its own (“We build” / “websites”), which a centered headline wants.' },
{ name: '--at-slot-align', fallback: 'start', note: 'Where each option sits in the slot, which is as wide as the widest: `start` for a slot at the end of a line, `center` for a slot on its own line.' },
{ name: '--at-wrap', fallback: 'normal', note: 'white-space. `nowrap` keeps a line whole, as distil usually wants.' },
{ name: '--at-accent', fallback: 'currentColor', note: 'The caret, the underline and the strike line.' },
@@ -1312,7 +1312,7 @@ import AnimatedText from '../components/AnimatedText.astro';
'A hand-written cookie banner that loads Google Analytics before the visitor clicks anything: it asks, but the tracking has already happened',
],
goodFor:
- 'A site that runs Google Analytics, or anything else, through Google Tag Manager and wants the consent choice on its own page, in its own colours, with no subscription. In opt-out mode US visitors are measured until they decline; with strictRegions on (the default), visitors in EEA, UK and Swiss time zones get prior consent, so nothing is fetched from Google until they accept. That region test reads the browser’s time zone: no IP lookup, no network call, and a VPN or a traveller can be misread either way.',
+ 'A site that runs Google Analytics, or anything else, through Google Tag Manager and wants the consent choice on its own page, in its own colors, with no subscription. In opt-out mode US visitors are measured until they decline; with strictRegions on (the default), visitors in EEA, UK and Swiss time zones get prior consent, so nothing is fetched from Google until they accept. That region test reads the browser’s time zone: no IP lookup, no network call, and a VPN or a traveler can be misread either way.',
notFor:
'A site that sets no cookies and loads no third-party tags. It needs no banner at all: leave gtmId empty and this renders nothing. Its job is gating Tag Manager, not decoration. Also not for sites that need per-category toggles, a stored consent log to show an auditor, or IAB TCF signals for ad networks: it is one yes-or-no for analytics, with ad storage denied unless enableAds is set. It does not make a site compliant by itself. It implements prior consent where that is required; the privacy policy, and what the site promises in it, are the site owner’s.',
props: [
@@ -1332,7 +1332,7 @@ import AnimatedText from '../components/AnimatedText.astro';
{ name: '--cc-text', fallback: '#4c5a60', note: 'Body copy at 13.5px: needs 4.5:1 on --cc-bg (default 6.33:1).' },
{ name: '--cc-ink', fallback: '#1d2b30', note: 'Decline label and its hover border.' },
{ name: '--cc-link', fallback: '#1f4f55', note: 'Privacy Policy link.' },
- { name: '--cc-accent', fallback: '#1f4f55', note: 'Accept/OK fill. Needs 4.5:1 against --cc-accent-ink (default 9.10:1): use the site’s dark surface tone, not its CTA colour.' },
+ { name: '--cc-accent', fallback: '#1f4f55', note: 'Accept/OK fill. Needs 4.5:1 against --cc-accent-ink (default 9.10:1): use the site’s dark surface tone, not its CTA color.' },
{ name: '--cc-accent-hover', fallback: '#143a3f', note: 'Accept/OK hover fill.' },
{ name: '--cc-accent-ink', fallback: '#fff', note: 'Accept/OK label.' },
{ name: '--cc-shadow', fallback: '0 14px 40px rgba(16, 32, 36, .18)', note: 'Bar shadow.' },
@@ -1345,7 +1345,7 @@ import AnimatedText from '../components/AnimatedText.astro';
'The bar is a non-modal dialog named “Cookie consent”. It does not take focus or trap it, and the page stays usable while it is open. Sites mount it at the end of the body, so keyboard users reach it after the page content.',
'It stays hidden (the hidden attribute) until the script has chosen the wording for the visitor’s mode, and is hidden again after a choice, so a screen reader never reads a bar that is not on screen.',
'Any element with data-cc-reopen (the “Cookie settings” link in a site’s footer) opens it again, so the choice can be changed at any time. There is no Escape shortcut: the choice is made with the buttons.',
- 'No movement: only 0.15s colour transitions on hover. Default text and button colours clear 4.5:1; a site that themes it must keep them there.',
+ 'No movement: only 0.15s color transitions on hover. Default text and button colors clear 4.5:1; a site that themes it must keep them there.',
],
usage: `---
// src/layouts/BaseLayout.astro, as on superherotech.ai. Do not copy CookieConsent.astro by hand:
@@ -1409,7 +1409,7 @@ import { analytics } from '../data/site';
{ name: 'speed', type: 'number', default: '1', note: 'Multiplier on the preset’s own pace; the scroll presets take 40 s per tile at 1.' },
{ name: 'minWidth', type: 'number', default: '700', note: 'Below this viewport width the still frame is drawn and nothing animates. 0 animates everywhere. Tracked live.' },
{ name: 'motion', type: "'auto' | 'off'", default: "'auto'", note: 'off draws the still frame always; such an instance does not count against one-per-page.' },
- { name: 'pointer', type: "'drift' | 'repel' | 'none'", default: "'drift'", note: 'How the canvas presets answer the mouse: waves lean and swell toward its x, cells and bubbles lean in or away, fog slides in parallax, rings and the halo centre trail it, birds are drawn or scattered, snow gusts with a sweep. Fine pointers only (hover + pointer: fine), never on touch; no reaction under reduced motion, with motion="off" or below minWidth. Listeners sit on the host, the canvas keeps pointer-events: none. The scroll presets do not react.' },
+ { name: 'pointer', type: "'drift' | 'repel' | 'none'", default: "'drift'", note: 'How the canvas presets answer the mouse: waves lean and swell toward its x, cells and bubbles lean in or away, fog slides in parallax, rings and the halo center trail it, birds are drawn or scattered, snow gusts with a sweep. Fine pointers only (hover + pointer: fine), never on touch; no reaction under reduced motion, with motion="off" or below minWidth. Listeners sit on the host, the canvas keeps pointer-events: none. The scroll presets do not react.' },
{ name: 'texture', type: 'string', note: 'scroll-x / scroll-y only: URL of the host’s own square, seamlessly tiling image, drawn at 512 px, in place of the generated one. Never a client image on a shared page.' },
{ name: 'class', type: 'string', note: 'Class for the host to position and stack it with.' },
],
@@ -1483,7 +1483,7 @@ import { analytics } from '../data/site';
{ name: '--md-radius', fallback: '12px', note: 'Box corners; size="full" has none.' },
{ name: '--md-padding', fallback: 'clamp(1.25rem, 4vw, 2rem)', note: 'Inside the box.' },
{ name: '--md-shadow', fallback: '0 1.5rem 4rem rgb(0 0 0 / 0.3)', note: 'Box shadow.' },
- { name: '--md-close-bg', fallback: 'transparent', note: 'Close button fill (a faint grey on hover).' },
+ { name: '--md-close-bg', fallback: 'transparent', note: 'Close button fill (a faint gray on hover).' },
{ name: '--md-close-fg', fallback: 'currentColor', note: 'Close icon.' },
{ name: '--md-focus', fallback: 'currentColor', note: 'Focus ring on the close button.' },
{ name: '--md-width-sm', fallback: '24rem', note: 'Max width of size="sm".' },
@@ -1547,7 +1547,7 @@ import { analytics } from '../data/site';
{ name: 'countdown', type: "'YYYY-MM-DDTHH:MM'", note: '“Ends in 2 days 4 hours” after the text, refreshed on the minute; the bar hides when it passes.' },
{ name: 'timeZone', type: 'string (IANA)', note: 'Required with from, until or countdown: “America/Chicago”, not the visitor’s zone.' },
{ name: 'endsIn / ends', type: 'string', default: '“Ends in” / “Ends”', note: 'Words before the countdown, and before the end date shown without JavaScript. The units (days, hours, minutes) are English.' },
- { name: 'theme', type: "'accent' | 'dark' | 'light'", default: "'accent'", note: 'Which pair of colours.' },
+ { name: 'theme', type: "'accent' | 'dark' | 'light'", default: "'accent'", note: 'Which pair of colors.' },
{ name: 'dismissLabel', type: 'string', default: '“Dismiss announcement”', note: 'Name of the dismiss button.' },
{ name: 'class', type: 'string', note: 'Class on the bar.' },
],
@@ -1558,7 +1558,7 @@ import { analytics } from '../data/site';
{ name: '--anb-dark-fg', fallback: '#fff', note: 'theme="dark" text.' },
{ name: '--anb-light-bg', fallback: '#eef2f1', note: 'theme="light" ground (12.92:1).' },
{ name: '--anb-light-fg', fallback: '#1d2b30', note: 'theme="light" text.' },
- { name: '--anb-link', fallback: 'currentColor', note: 'Link colour; underlined either way. If you set it, keep 4.5:1 on the ground.' },
+ { name: '--anb-link', fallback: 'currentColor', note: 'Link color; underlined either way. If you set it, keep 4.5:1 on the ground.' },
{ name: '--anb-min-height', fallback: '2.75rem', note: 'Bar height: the 44px dismiss button.' },
{ name: '--anb-font-size', fallback: '0.95rem', note: 'Text size.' },
{ name: '--anb-focus', fallback: 'currentColor', note: 'Focus ring on the link and the dismiss button.' },
@@ -1568,7 +1568,7 @@ import { analytics } from '../data/site';
'A region (role="region") named by its own text, or by `label`. The link is a normal link.',
'The dismiss button is a real 44px named “Dismiss announcement”. After a dismiss, focus moves to the next focusable thing on the page, not to nowhere.',
'The countdown is a with the absolute end, and deliberately not a live region: it changes every minute and should not be read out each time.',
- 'The fallback colours of all three themes clear 4.5:1 (7.27, 14.75 and 12.92 to 1); `npm run check` computes them.',
+ 'The fallback colors of all three themes clear 4.5:1 (7.27, 14.75 and 12.92 to 1); `npm run check` computes them.',
'prefers-reduced-motion: a dismissed bar disappears at once instead of collapsing.',
'Without JavaScript the bar shows when the build put it inside its window, the countdown gives the end date instead, and it cannot be dismissed: the button stays hidden rather than doing nothing.',
],
@@ -1654,7 +1654,7 @@ import { analytics } from '../data/site';
src="/images/showroom.webp" width={1600} height={1000}
alt="The showroom: a sofa by the window, a lamp and a plant"
points={[
- { x: 50, y: 62, title: 'Three-seat sofa', text: 'Washable covers in twelve colours.' },
+ { x: 50, y: 62, title: 'Three-seat sofa', text: 'Washable covers in twelve colors.' },
{ x: 73, y: 25, title: 'Arc lamp', text: 'Warm light on a dimmer.' },
{ x: 87, y: 58, title: 'Rubber plant', text: 'Copes with low light.', image: '/images/plant.webp', imageAlt: '' },
]}
@@ -1671,7 +1671,7 @@ import { analytics } from '../data/site';
name: 'Info circle',
aka: ['UABB Info Circle', 'circular infographic', 'circle infographic', 'process circle', 'cycle diagram', 'radial menu', 'interactive infographic'],
summary:
- 'Items (an icon or a round image each) spaced evenly round a circle, positions computed at build; the chosen item’s title and text show in the middle. Hover, focus and click choose; each item is a button with aria-pressed and the centre is a polite live region. Optional autoplay with a pause button. In a container narrower than 28rem, and without JavaScript, it is a plain list with every item’s text showing.',
+ 'Items (an icon or a round image each) spaced evenly round a circle, positions computed at build; the chosen item’s title and text show in the middle. Hover, focus and click choose; each item is a button with aria-pressed and the center is a polite live region. Optional autoplay with a pause button. In a container narrower than 28rem, and without JavaScript, it is a plain list with every item’s text showing.',
pitch: 'Five things you do, round one circle: point at one and it tells its story in the middle — and on a phone it is simply a list.',
// SE Ranking US, 2026-09-26: circular infographic 320/mo, difficulty 7 (circle infographic
// and infographic circle are the same cluster, 320/7); info circle 40/10; interactive
@@ -1701,7 +1701,7 @@ import { analytics } from '../data/site';
{ name: '--inc-item-fg', fallback: 'var(--inc-accent)', note: 'Its icon.' },
{ name: '--inc-active-fg', fallback: '#fff', note: 'The chosen item’s icon (7.27:1 on the accent).' },
{ name: '--inc-centre-bg', fallback: '#f4f1fe', note: 'The disc behind the text in the middle, and behind list icons.' },
- { name: '--inc-fg', fallback: '#1e283c', note: 'Text (13.24:1 on the centre disc).' },
+ { name: '--inc-fg', fallback: '#1e283c', note: 'Text (13.24:1 on the center disc).' },
{ name: '--inc-ring', fallback: 'rgb(89 51 216 / 0.35)', note: 'The dashed circumference.' },
{ name: '--inc-size', fallback: '32rem', note: 'Diameter; the `size` prop sets it.' },
{ name: '--inc-item-size', fallback: '4.5rem', note: 'Each item’s disc (never under 44px).' },
@@ -1875,7 +1875,7 @@ import { analytics } from '../data/site';
'Without JavaScript: the static line “Ends Oct 3, 5:00 PM CDT” in a (an evergreen one reads “Ends in 30 minutes”). The digits from the build’s clock are not shown, because they would be stale and never tick.',
'No layout shift: the script placed right after the element shows and corrects the digits before the first paint.',
'prefers-reduced-motion: no tick animation on a changing digit and no ring transition; the numbers simply change.',
- 'The square style’s fallback colours clear 4.5:1 (14.75:1); `npm run check` computes it.',
+ 'The square style’s fallback colors clear 4.5:1 (14.75:1); `npm run check` computes it.',
],
usage: `
The autumn sale ends in
@@ -1898,7 +1898,7 @@ import { analytics } from '../data/site';
name: 'Content toggle',
aka: ['UABB Content Toggle', 'PowerPack Content Toggle', 'pricing toggle', 'monthly / annual switch', 'content switcher', 'toggle switch'],
summary:
- 'Two labelled versions of one thing, a switch (role="switch") or two pressed buttons between them, and two panels in slots a and b. Optional badge on one label, a choice remembered for the session, and ? = to preselect. Without JavaScript both panels show under their labels as headings.',
+ 'Two labeled versions of one thing, a switch (role="switch") or two pressed buttons between them, and two panels in slots a and b. Optional badge on one label, a choice remembered for the session, and ? = to preselect. Without JavaScript both panels show under their labels as headings.',
pitch: 'Let visitors flip between two versions of the same thing — monthly or annual prices, homes or businesses — without leaving the page.',
// SE Ranking US, 2026-09-26: toggle content 110/mo, difficulty 27; content switcher 90/18;
// css toggle switch 170/16; html toggle switch 90/9. "content toggle" and "pricing toggle"
@@ -1946,7 +1946,7 @@ import { analytics } from '../data/site';
'The badge describes the switch (aria-describedby), so “Save 20%” is heard as well as seen.',
'The shown panel keeps its label as a heading for screen readers only, so it is clear which version is on the page; the other panel is hidden, not just covered.',
'Without JavaScript both panels render, each under its label as a heading, and the switch (which could not work) is not shown.',
- 'No layout shift: the script right after the element settles it before the first paint. prefers-reduced-motion: the knob jumps instead of sliding and the panel does not fade. In forced-colours mode the track and knob are drawn with system colours.',
+ 'No layout shift: the script right after the element settles it before the first paint. prefers-reduced-motion: the knob jumps instead of sliding and the panel does not fade. In forced-colors mode the track and knob are drawn with system colors.',
],
usage: `
@@ -1999,7 +1999,7 @@ import { analytics } from '../data/site';
{ name: '--ofc-backdrop', fallback: 'rgb(15 20 35 / 0.55)', note: 'Behind a modal panel.' },
{ name: '--ofc-shadow', fallback: '0 0 3rem rgb(0 0 0 / 0.25)', note: 'The panel’s shadow.' },
{ name: '--ofc-padding', fallback: 'clamp(1rem, 4vw, 1.5rem)', note: 'Inside the panel.' },
- { name: '--ofc-close-bg', fallback: 'transparent', note: 'Close button fill (a faint grey on hover).' },
+ { name: '--ofc-close-bg', fallback: 'transparent', note: 'Close button fill (a faint gray on hover).' },
{ name: '--ofc-close-fg', fallback: 'currentColor', note: 'Close icon.' },
{ name: '--ofc-focus', fallback: 'currentColor', note: 'Focus ring inside the panel.' },
{ name: '--ofc-z', fallback: '110', note: 'Stacking of a push panel, below the cookie-consent bar (120). A modal panel is in the browser’s top layer, like every modal dialog.' },
@@ -2073,15 +2073,15 @@ import { analytics } from '../data/site';
{ name: '--vg-gap', fallback: '1.25rem', note: 'Gap between tiles.' },
{ name: '--vg-template', fallback: '(unset)', note: 'The whole grid-template-columns, when the host wants its own grid: `repeat(4, 1fr)`.' },
{ name: '--vg-radius', fallback: '0.5rem', note: 'Corner radius of each frame.' },
- { name: '--vg-bg', fallback: '#000', note: 'Frame colour behind the poster and the player.' },
+ { name: '--vg-bg', fallback: '#000', note: 'Frame color behind the poster and the player.' },
{ name: '--vg-panel', fallback: 'linear-gradient(135deg, #2c3656, #1e283c)', note: 'A tile without a poster.' },
{ name: '--vg-overlay', fallback: 'rgb(0 0 0 / 0.12)', note: 'Tint over the poster.' },
{ name: '--vg-play-bg', fallback: 'rgb(0 0 0 / 0.7)', note: 'Play button circle. Keep 3:1 against the posters.' },
{ name: '--vg-play-fg', fallback: '#fff', note: 'Play icon.' },
{ name: '--vg-play-size', fallback: '3.5rem', note: 'Diameter of the circle.' },
{ name: '--vg-focus', fallback: '#5933d8', note: 'Focus ring on tiles and chips.' },
- { name: '--vg-title', fallback: 'inherit', note: 'Tile title colour.' },
- { name: '--vg-caption', fallback: 'inherit', note: 'Caption colour.' },
+ { name: '--vg-title', fallback: 'inherit', note: 'Tile title color.' },
+ { name: '--vg-caption', fallback: 'inherit', note: 'Caption color.' },
{ name: '--vg-chip-bg', fallback: '#fff', note: 'Chip fill.' },
{ name: '--vg-chip-fg', fallback: '#1e283c', note: 'Chip text (14.75:1 on the fallback fill).' },
{ name: '--vg-chip-border', fallback: '#c9cedb', note: 'Chip outline.' },
@@ -2093,8 +2093,8 @@ import { analytics } from '../data/site';
],
a11y: [
'Each tile’s play control is a real named “Play: ”, over the whole poster, with a visible focus ring. The title is also printed under the tile as a heading (titleTag).',
- 'Filter chips are a labelled group of , “All” first and pressed. Choosing one hides the other tiles and a polite live region says how many are shown (“Open films: 3 videos.”).',
- 'Lightbox: one native modal , named by the video’s title: focus trapped, Escape and a backdrop click close it, the close button is labelled. On open the video starts and focus goes to the close button, because a YouTube or Vimeo iframe keeps every key, Escape included; Tab moves on into the player. Closing empties the dialog so the sound stops, returns focus to the tile and releases the scroll lock.',
+ 'Filter chips are a labeled group of , “All” first and pressed. Choosing one hides the other tiles and a polite live region says how many are shown (“Open films: 3 videos.”).',
+ 'Lightbox: one native modal , named by the video’s title: focus trapped, Escape and a backdrop click close it, the close button is labeled. On open the video starts and focus goes to the close button, because a YouTube or Vimeo iframe keeps every key, Escape included; Tab moves on into the player. Closing empties the dialog so the sound stops, returns focus to the tile and releases the scroll lock.',
'Inline: focus moves into the player (the titled iframe, or the ). One tile plays at a time.',
'Self-hosted files keep the browser’s native controls with a captions menu. A captions track is expected (WCAG 1.2.2); the build warns without one.',
'prefers-reduced-motion: tiles shown by a filter appear at once instead of fading in, and the play button does not grow on hover. Nothing plays until asked.',
@@ -2111,7 +2111,7 @@ import { analytics } from '../data/site';
/>
`,
license:
- 'The videos stay yours, or their owners’: the gallery plays them from YouTube, Vimeo or your own site, under those services’ terms, and adds nothing of its own. The films in our demo are the Blender Foundation’s open films Big Buck Bunny and Sintel, CC BY 3.0, credited under each one as that licence asks.',
+ 'The videos stay yours, or their owners’: the gallery plays them from YouTube, Vimeo or your own site, under those services’ terms, and adds nothing of its own. The films in our demo are the Blender Foundation’s open films Big Buck Bunny and Sintel, CC BY 3.0, credited under each one as that license asks.',
usedOn: [{ site: 'superherotech.ai', where: '/elements/video-gallery/ (demo)' }],
file: 'src/library/video-gallery/VideoGallery.astro',
added: '2026-09-26',
@@ -2133,7 +2133,7 @@ import { analytics } from '../data/site';
replaces: ['UABB / PowerPack “Google Map” modules (Beaver Builder)', 'Elementor’s Google Maps widget', 'WP Google Maps and similar plugins', 'a Google Maps