@hubpav/hwio-ui 0.7.11 → 0.9.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/package.json +4 -4
- package/src/components/HWioApplicationTile.astro +49 -0
- package/src/components/HWioButton.astro +9 -4
- package/src/components/HWioChoiceCards.astro +3 -3
- package/src/components/HWioContactSection.astro +1 -1
- package/src/components/HWioCookieConsent.astro +9 -9
- package/src/components/HWioCutout.astro +58 -0
- package/src/components/HWioFaq.astro +1 -1
- package/src/components/HWioFeatureCard.astro +1 -1
- package/src/components/HWioFooter.astro +7 -4
- package/src/components/HWioHeader.astro +13 -6
- package/src/components/HWioHeroSplit.astro +14 -7
- package/src/components/HWioIsoScene.astro +93 -0
- package/src/components/HWioKeyFigure.astro +45 -0
- package/src/components/HWioLanguageSwitcher.astro +11 -1
- package/src/components/HWioLogoStrip.astro +1 -1
- package/src/components/HWioMegaLinks.astro +54 -0
- package/src/components/HWioPhoto.astro +36 -0
- package/src/components/HWioPricingTiers.astro +1 -1
- package/src/components/HWioProcessSteps.astro +1 -1
- package/src/components/HWioReveal.astro +15 -0
- package/src/components/HWioSection.astro +1 -0
- package/src/components/HWioStatsBand.astro +2 -2
- package/src/components/HWioStoryCard.astro +38 -0
- package/src/styles/prose.css +3 -3
- package/src/styles/styles.css +325 -37
- package/src/styles/themes/er3o.css +92 -1
- package/src/styles/themes/hwio-dark.css +127 -19
- package/src/styles/themes/hwio-forestry-dark.css +127 -19
- package/src/styles/themes/hwio-forestry.css +121 -13
- package/src/styles/themes/hwio.css +122 -14
- package/src/types.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubpav/hwio-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "HARDWARIO web design system for Astro: DaisyUI 5 themes generated from the brand tokens, the Tailwind entry stylesheet and the HWio* components.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"test": "node --test \"test/**/*.test.mjs\""
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@hubpav/hwio-brand": "0.
|
|
36
|
-
"@hubpav/hwio-web-runtime": "0.
|
|
37
|
-
"@hubpav/hwio-web-worker": "0.
|
|
35
|
+
"@hubpav/hwio-brand": "0.9.0",
|
|
36
|
+
"@hubpav/hwio-web-runtime": "0.9.0",
|
|
37
|
+
"@hubpav/hwio-web-worker": "0.9.0",
|
|
38
38
|
"daisyui": "5.7.47"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Application tile: a picture, a title of three or four words and one line, optionally the references
|
|
4
|
+
* that run it. The whole tile links to the application page (its title underlines on hover and focus).
|
|
5
|
+
* On phones the tile becomes a row (thumbnail beside the text), so a set of eight stays one short list.
|
|
6
|
+
* `cutout` shows a product cut-out whole on the Panel ground (with an optional isometric `backdrop`)
|
|
7
|
+
* instead of a photograph. `reveal` fades the tile in once in view (needs HWioReveal on the page);
|
|
8
|
+
* `revealDelay` (ms) staggers a row.
|
|
9
|
+
*/
|
|
10
|
+
import type { HWioBackdrop, HWioMedia, HWioTrack } from '../types';
|
|
11
|
+
import HWioCutout from './HWioCutout.astro';
|
|
12
|
+
import HWioPhoto from './HWioPhoto.astro';
|
|
13
|
+
interface Props {
|
|
14
|
+
href: string;
|
|
15
|
+
title: string;
|
|
16
|
+
line: string;
|
|
17
|
+
image: HWioMedia;
|
|
18
|
+
cutout?: boolean;
|
|
19
|
+
backdrop?: HWioBackdrop;
|
|
20
|
+
/** Names of the customers or projects that run it, shown under the line, separated by middle dots. */
|
|
21
|
+
references?: string[];
|
|
22
|
+
headingLevel?: 'h2' | 'h3' | 'h4';
|
|
23
|
+
track?: HWioTrack;
|
|
24
|
+
reveal?: boolean;
|
|
25
|
+
revealDelay?: number;
|
|
26
|
+
eager?: boolean;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
const { href, title, line, image, cutout = false, backdrop, references = [], headingLevel = 'h3', track, reveal = false, revealDelay, eager = false, class: className = '' } = Astro.props;
|
|
30
|
+
const Heading = headingLevel;
|
|
31
|
+
const hover = 'transition-transform duration-300 ease-out group-hover:scale-[1.02] motion-reduce:transition-none';
|
|
32
|
+
---
|
|
33
|
+
<a
|
|
34
|
+
href={href}
|
|
35
|
+
class:list={['hwio-card-link group grid grid-cols-[6rem_minmax(0,1fr)] content-start items-center gap-4 text-base-content sm:grid-cols-1 sm:items-start sm:gap-5', className]}
|
|
36
|
+
data-track={track?.id}
|
|
37
|
+
data-track-location={track?.location}
|
|
38
|
+
data-hwio-reveal={reveal ? 'true' : undefined}
|
|
39
|
+
data-hwio-reveal-delay={reveal && revealDelay ? String(revealDelay) : undefined}
|
|
40
|
+
>
|
|
41
|
+
{cutout
|
|
42
|
+
? <HWioCutout image={image} aspect="4/3" rounded="xl" inset="lg" backdrop={backdrop} eager={eager} imgClass={hover} />
|
|
43
|
+
: <HWioPhoto image={image} aspect="4/3" rounded="xl" eager={eager} imgClass={hover} />}
|
|
44
|
+
<div class="min-w-0">
|
|
45
|
+
<Heading class="hwio-role-title hwio-card-title">{title}</Heading>
|
|
46
|
+
<p class="hwio-muted mt-1.5 text-[0.9375rem] leading-[1.5]">{line}</p>
|
|
47
|
+
{references.length > 0 && <p class="hwio-muted mt-3 text-[0.8125rem] leading-[1.4]">{references.join(' · ')}</p>}
|
|
48
|
+
</div>
|
|
49
|
+
</a>
|
|
@@ -4,6 +4,9 @@ import HWioIcon from './HWioIcon.astro';
|
|
|
4
4
|
/**
|
|
5
5
|
* Link or button on DaisyUI `btn`. Labels are slot content (sites own their copy; owner rule:
|
|
6
6
|
* imperative verb, no article). `track` writes the measurement attributes data-track / data-track-location.
|
|
7
|
+
* `arrow` (the page's primary call to action only) adds an arrow that nudges 3 px on hover and focus
|
|
8
|
+
* (.hwio-arrow; no nudge under reduced motion). An external link shows the external glyph after the label
|
|
9
|
+
* unless `externalIcon={false}` (HWioHeader's icon-only CTAs place it beside the label themselves).
|
|
7
10
|
*/
|
|
8
11
|
interface Props {
|
|
9
12
|
href?: string;
|
|
@@ -12,6 +15,8 @@ interface Props {
|
|
|
12
15
|
size?: 'sm' | 'md' | 'lg' | 'wide';
|
|
13
16
|
track?: HWioTrack;
|
|
14
17
|
arrow?: boolean;
|
|
18
|
+
/** Show the external-link glyph after the label of an external link (default true). */
|
|
19
|
+
externalIcon?: boolean;
|
|
15
20
|
type?: 'button' | 'submit';
|
|
16
21
|
class?: string;
|
|
17
22
|
[key: string]: unknown;
|
|
@@ -25,19 +30,19 @@ const VARIANT = {
|
|
|
25
30
|
link: 'btn btn-link',
|
|
26
31
|
} as const;
|
|
27
32
|
const SIZE = { sm: 'btn-sm', md: '', lg: 'btn-lg', wide: 'btn-wide' } as const;
|
|
28
|
-
const { href, external = false, variant = 'primary', size = 'md', track, arrow = false, type = 'button', class: className = '', ...rest } = Astro.props;
|
|
33
|
+
const { href, external = false, variant = 'primary', size = 'md', track, arrow = false, externalIcon = true, type = 'button', class: className = '', ...rest } = Astro.props;
|
|
29
34
|
const classes = [VARIANT[variant], SIZE[size], className];
|
|
30
35
|
const trackAttrs = track ? { 'data-track': track.id, 'data-track-location': track.location } : {};
|
|
31
36
|
---
|
|
32
37
|
{href ? (
|
|
33
38
|
<a href={href} class:list={classes} target={external ? '_blank' : undefined} rel={external ? 'noopener noreferrer' : undefined} {...trackAttrs} {...rest}>
|
|
34
39
|
<slot />
|
|
35
|
-
{arrow && <HWioIcon name="arrow-right" size="sm" />}
|
|
36
|
-
{external && !arrow && <HWioIcon name="external" size="sm" />}
|
|
40
|
+
{arrow && <HWioIcon name="arrow-right" size="sm" class="hwio-arrow" />}
|
|
41
|
+
{external && externalIcon && !arrow && <HWioIcon name="external" size="sm" />}
|
|
37
42
|
</a>
|
|
38
43
|
) : (
|
|
39
44
|
<button type={type} class:list={classes} {...trackAttrs} {...rest}>
|
|
40
45
|
<slot />
|
|
41
|
-
{arrow && <HWioIcon name="arrow-right" size="sm" />}
|
|
46
|
+
{arrow && <HWioIcon name="arrow-right" size="sm" class="hwio-arrow" />}
|
|
42
47
|
</button>
|
|
43
48
|
)}
|
|
@@ -15,12 +15,12 @@ const cols = COLS[(Math.min(Math.max(items.length, 1), 3) as 1 | 2 | 3)];
|
|
|
15
15
|
return (
|
|
16
16
|
<article class:list={['card card-border flex h-full flex-col bg-base-100 border-t-[3px]', isLead ? 'border-t-accent' : 'border-t-base-300']}>
|
|
17
17
|
<div class="card-body gap-0 p-10">
|
|
18
|
-
<p class:list={['mb-4 text-sm
|
|
19
|
-
<h3 class="mb-3.5
|
|
18
|
+
<p class:list={['hwio-weight-label mb-4 text-sm', isLead ? 'text-accent' : 'hwio-muted']}>{c.label}</p>
|
|
19
|
+
<h3 class="hwio-choice-title mb-3.5">{c.title}</h3>
|
|
20
20
|
<p class="hwio-muted mb-6">{c.text}</p>
|
|
21
21
|
{c.points && c.points.length > 0 && (
|
|
22
22
|
<ul class="hwio-muted mb-8 flex flex-col gap-3 text-[15px]">
|
|
23
|
-
{c.points.map((pt) => <li class="flex items-start gap-3"><HWioIcon name="check" class="mt-0.5 shrink-0
|
|
23
|
+
{c.points.map((pt) => <li class="flex items-start gap-3"><HWioIcon name="check" class="hwio-icon mt-0.5 shrink-0" />{pt}</li>)}
|
|
24
24
|
</ul>
|
|
25
25
|
)}
|
|
26
26
|
<div class="mt-auto"><HWioButton href={c.cta.href} external={c.cta.external} variant={isLead ? 'primary' : 'secondary'} arrow={isLead} track={c.cta.track}>{c.cta.label}</HWioButton></div>
|
|
@@ -23,7 +23,7 @@ const { id = 'contact', eyebrow, title, lead, person, tone = 'alt', class: class
|
|
|
23
23
|
<div class="mt-8 flex items-center gap-4">
|
|
24
24
|
{person.image && <img src={person.image.src} alt={person.image.alt} width={person.image.width} height={person.image.height} class="size-16 rounded-full object-cover" loading="lazy" />}
|
|
25
25
|
<div class="text-sm">
|
|
26
|
-
<p class="
|
|
26
|
+
<p class="hwio-weight-label">{person.name}</p>
|
|
27
27
|
{person.role && <p class="opacity-80">{person.role}</p>}
|
|
28
28
|
{person.email && <a href={`mailto:${person.email}`} class="link link-hover inline-flex items-center gap-1"><HWioIcon name="mail" size="sm" />{person.email}</a>}
|
|
29
29
|
{person.phone && <a href={person.phoneHref ?? `tel:${person.phone.replace(/\s+/g, '')}`} class="link link-hover ml-3 inline-flex items-center gap-1 whitespace-nowrap"><HWioIcon name="phone" size="sm" />{person.phone}</a>}
|
|
@@ -24,10 +24,10 @@ const optional = ['preferences', 'statistics', 'marketing'] as const;
|
|
|
24
24
|
<div class="card card-border mx-auto max-w-5xl bg-base-100 shadow-2xl">
|
|
25
25
|
<div class="card-body gap-4 md:flex-row md:items-center">
|
|
26
26
|
<div class="min-w-0 flex-1">
|
|
27
|
-
<p class="text-base
|
|
28
|
-
<p class="mt-1 text-sm leading-6
|
|
27
|
+
<p class="hwio-weight-label text-base">{labels.title}</p>
|
|
28
|
+
<p class="hwio-note mt-1 text-sm leading-6">
|
|
29
29
|
{labels.short}
|
|
30
|
-
<a href={policyHref} class="link link
|
|
30
|
+
<a href={policyHref} class="link hwio-link hwio-weight-label">{labels.policy}</a>
|
|
31
31
|
</p>
|
|
32
32
|
</div>
|
|
33
33
|
<div class="flex flex-col gap-2 sm:flex-row md:shrink-0">
|
|
@@ -45,8 +45,8 @@ const optional = ['preferences', 'statistics', 'marketing'] as const;
|
|
|
45
45
|
<div class="card-body gap-6">
|
|
46
46
|
<div class="flex items-start justify-between gap-6">
|
|
47
47
|
<div>
|
|
48
|
-
<h2 id="hwio-consent-settings-title" class="text-2xl
|
|
49
|
-
<p class="mt-2 text-sm leading-6
|
|
48
|
+
<h2 id="hwio-consent-settings-title" class="text-2xl">{labels.settingsTitle}</h2>
|
|
49
|
+
<p class="hwio-note mt-2 text-sm leading-6">{labels.settingsIntro}</p>
|
|
50
50
|
</div>
|
|
51
51
|
<button type="button" data-hwio-consent-close class="btn btn-ghost btn-square btn-sm" aria-label={labels.close}>
|
|
52
52
|
<svg class="size-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
|
|
@@ -56,8 +56,8 @@ const optional = ['preferences', 'statistics', 'marketing'] as const;
|
|
|
56
56
|
<div class="rounded-box border border-base-300 p-4">
|
|
57
57
|
<div class="flex items-start justify-between gap-4">
|
|
58
58
|
<div>
|
|
59
|
-
<p class="
|
|
60
|
-
<p class="mt-1 text-sm leading-6
|
|
59
|
+
<p class="hwio-weight-label">{labels.categories.necessary.title}</p>
|
|
60
|
+
<p class="hwio-note mt-1 text-sm leading-6">{labels.categories.necessary.description}</p>
|
|
61
61
|
</div>
|
|
62
62
|
<span class="badge badge-ghost shrink-0">{labels.alwaysOn}</span>
|
|
63
63
|
</div>
|
|
@@ -65,8 +65,8 @@ const optional = ['preferences', 'statistics', 'marketing'] as const;
|
|
|
65
65
|
{optional.map((category) => (
|
|
66
66
|
<label class="flex cursor-pointer items-start justify-between gap-4 rounded-box border border-base-300 p-4 transition-colors hover:border-secondary">
|
|
67
67
|
<span>
|
|
68
|
-
<span class="block
|
|
69
|
-
<span class="mt-1 block text-sm leading-6
|
|
68
|
+
<span class="hwio-weight-label block">{labels.categories![category].title}</span>
|
|
69
|
+
<span class="hwio-note mt-1 block text-sm leading-6">{labels.categories![category].description}</span>
|
|
70
70
|
</span>
|
|
71
71
|
<input type="checkbox" class="toggle toggle-primary mt-1 shrink-0" id={`hwio-consent-${category}`} name={`hwio-consent-${category}`} autocomplete="off" data-hwio-consent-toggle={category} />
|
|
72
72
|
</label>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* A product cut-out (transparent ground) shown whole in a fixed frame, with the one shadow recipe every
|
|
4
|
+
* product carries (.hwio-product-shadow: a tight contact shadow plus a soft ambient one; owner,
|
|
5
|
+
* 2026-10-30). `baseline` stands it on the frame's common bottom line with a blurred floor ellipse, so
|
|
6
|
+
* a row of products lines up as one family. `ground="panel"` puts it on the Panel ground (cards,
|
|
7
|
+
* tiles), `plain` on the page. `backdrop` places it in front of a quiet isometric room in light tones
|
|
8
|
+
* (the artwork library's backdrops, with their dark-theme drawing) when no field photograph exists.
|
|
9
|
+
*/
|
|
10
|
+
import type { HWioAspect, HWioBackdrop, HWioMedia } from '../types';
|
|
11
|
+
interface Props {
|
|
12
|
+
image: HWioMedia;
|
|
13
|
+
aspect?: HWioAspect;
|
|
14
|
+
ground?: 'panel' | 'plain';
|
|
15
|
+
baseline?: boolean;
|
|
16
|
+
/** Air around the product inside the frame: sm 8 %, md 10 %, lg 12 % of the frame width. */
|
|
17
|
+
inset?: 'sm' | 'md' | 'lg';
|
|
18
|
+
backdrop?: HWioBackdrop;
|
|
19
|
+
rounded?: 'none' | 'xl' | '2xl';
|
|
20
|
+
eager?: boolean;
|
|
21
|
+
class?: string;
|
|
22
|
+
imgClass?: string;
|
|
23
|
+
}
|
|
24
|
+
const { image, aspect = '1/1', ground = 'panel', baseline = false, inset = 'md', backdrop, rounded = '2xl', eager = false, class: className = '', imgClass = '' } = Astro.props;
|
|
25
|
+
const ASPECT = { '1/1': 'aspect-square', '4/3': 'aspect-[4/3]', '3/2': 'aspect-[3/2]', '16/9': 'aspect-video', '4/5': 'aspect-[4/5]', '3/4': 'aspect-[3/4]' } as const;
|
|
26
|
+
const ROUNDED = { none: 'rounded-none', xl: 'rounded-xl', '2xl': 'rounded-2xl' } as const;
|
|
27
|
+
const PAD = { sm: 'p-[8%]', md: 'p-[10%]', lg: 'p-[12%]' } as const;
|
|
28
|
+
// On a baseline the product sits 6 % higher than its inset, above the floor shadow, aligned to the bottom.
|
|
29
|
+
const PAD_BASELINE = { sm: 'px-[8%] pt-[8%] pb-[14%] object-bottom', md: 'px-[10%] pt-[10%] pb-[16%] object-bottom', lg: 'px-[12%] pt-[12%] pb-[18%] object-bottom' } as const;
|
|
30
|
+
const FLOOR = { sm: 'bottom-[10.5%]', md: 'bottom-[12.5%]', lg: 'bottom-[14.5%]' } as const;
|
|
31
|
+
// In front of a room the product stands a little smaller, the same on every such tile.
|
|
32
|
+
const PAD_BACKDROP = 'pt-[14%] px-[18%] pb-[10%]';
|
|
33
|
+
const BACKDROP_PAD_BASELINE = 'pt-[14%] px-[18%] pb-[10%] object-bottom';
|
|
34
|
+
const padding = backdrop ? (baseline ? BACKDROP_PAD_BASELINE : PAD_BACKDROP) : baseline ? PAD_BASELINE[inset] : PAD[inset];
|
|
35
|
+
---
|
|
36
|
+
<div class:list={['relative overflow-hidden', ASPECT[aspect], ROUNDED[rounded], ground === 'panel' && 'hwio-cutout-ground', className]}>
|
|
37
|
+
{backdrop && (backdrop.dark ? (
|
|
38
|
+
<>
|
|
39
|
+
<img src={backdrop.light} alt="" aria-hidden="true" loading={eager ? 'eager' : 'lazy'} decoding="async" class="hwio-backdrop hwio-backdrop-light" />
|
|
40
|
+
<img src={backdrop.dark} alt="" aria-hidden="true" loading="lazy" decoding="async" class="hwio-backdrop hwio-backdrop-dark" />
|
|
41
|
+
</>
|
|
42
|
+
) : (
|
|
43
|
+
<img src={backdrop.light} alt="" aria-hidden="true" loading={eager ? 'eager' : 'lazy'} decoding="async" class="hwio-backdrop" />
|
|
44
|
+
))}
|
|
45
|
+
<img
|
|
46
|
+
src={image.src}
|
|
47
|
+
srcset={image.srcset}
|
|
48
|
+
sizes={image.sizes}
|
|
49
|
+
alt={image.alt}
|
|
50
|
+
width={image.width}
|
|
51
|
+
height={image.height}
|
|
52
|
+
loading={eager ? 'eager' : 'lazy'}
|
|
53
|
+
decoding="async"
|
|
54
|
+
fetchpriority={eager ? 'high' : undefined}
|
|
55
|
+
class:list={['hwio-product-shadow absolute inset-0 z-[1] h-full w-full object-contain', padding, imgClass]}
|
|
56
|
+
/>
|
|
57
|
+
{baseline && <span class:list={['hwio-product-floor', FLOOR[inset]]} aria-hidden="true"></span>}
|
|
58
|
+
</div>
|
|
@@ -9,7 +9,7 @@ const ld = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: i
|
|
|
9
9
|
<div class:list={['space-y-3', className]}>
|
|
10
10
|
{items.map((item) => (
|
|
11
11
|
<details class="collapse collapse-arrow border border-base-300 bg-base-100" name={name}>
|
|
12
|
-
<summary class="collapse-title
|
|
12
|
+
<summary class="collapse-title hwio-weight-label">{item.q}</summary>
|
|
13
13
|
<div class="collapse-content space-y-3 opacity-90">{html ? <div set:html={paragraphs(item.a).join('')} /> : paragraphs(item.a).map((p) => <p>{p}</p>)}</div>
|
|
14
14
|
</details>
|
|
15
15
|
))}
|
|
@@ -6,7 +6,7 @@ const { title, text, icon, href, class: className = '' } = Astro.props;
|
|
|
6
6
|
<article class:list={['card card-border h-full bg-base-100', href ? 'transition-shadow hover:shadow-lg' : '', className]}>
|
|
7
7
|
<div class="card-body gap-0">
|
|
8
8
|
<slot name="icon">
|
|
9
|
-
{icon && <span class="mb-6 inline-flex size-12 items-center justify-center rounded-xl
|
|
9
|
+
{icon && <span class="hwio-icon-tile mb-6 inline-flex size-12 items-center justify-center rounded-xl"><HWioIcon name={icon as 'check'} size="lg" /></span>}
|
|
10
10
|
</slot>
|
|
11
11
|
<h3 class="mb-2.5">{href ? <a href={href} class="link link-hover">{title}</a> : title}</h3>
|
|
12
12
|
<p class="hwio-muted text-base">{text}</p>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Site footer on the neutral surface: social bar, logo slot, link columns, branches, legal row
|
|
4
4
|
* with the cookie-settings button, the cross-site strip, copyright and tagline. All strings are props.
|
|
5
|
+
* The bottom line (owner, 2026-10-30) centres the copyright and the tagline together, a middle dot
|
|
6
|
+
* between them; on phones they stack, centred, without the dot.
|
|
5
7
|
*/
|
|
6
8
|
import type { HWioLinkItem } from '../types';
|
|
7
9
|
import HWioIcon from './HWioIcon.astro';
|
|
@@ -44,7 +46,7 @@ const linkClass = 'inline-flex items-center gap-1 text-neutral-content/80 transi
|
|
|
44
46
|
</div>
|
|
45
47
|
{branches && (
|
|
46
48
|
<div>
|
|
47
|
-
<p class="mb-4
|
|
49
|
+
<p class="hwio-weight-footer-title mb-4">{branches.title}</p>
|
|
48
50
|
<div class="space-y-2 text-base">
|
|
49
51
|
{branches.items.map((b) => (
|
|
50
52
|
<div>
|
|
@@ -57,7 +59,7 @@ const linkClass = 'inline-flex items-center gap-1 text-neutral-content/80 transi
|
|
|
57
59
|
)}
|
|
58
60
|
{columns.map((col) => (
|
|
59
61
|
<div>
|
|
60
|
-
<p class="mb-4
|
|
62
|
+
<p class="hwio-weight-footer-title mb-4">{col.title}</p>
|
|
61
63
|
<ul class="space-y-1 text-base">
|
|
62
64
|
{col.links.map((l) => (
|
|
63
65
|
<li>
|
|
@@ -90,9 +92,10 @@ const linkClass = 'inline-flex items-center gap-1 text-neutral-content/80 transi
|
|
|
90
92
|
)}
|
|
91
93
|
|
|
92
94
|
<div class="border-t border-neutral-content/10">
|
|
93
|
-
<div class="container flex flex-col items-center justify-
|
|
95
|
+
<div class="container flex flex-col items-center justify-center gap-1 py-6 text-center text-sm opacity-80 md:flex-row md:gap-0">
|
|
94
96
|
<p>{copyright}</p>
|
|
95
|
-
{tagline && <
|
|
97
|
+
{tagline && <span aria-hidden="true" class="mx-3 hidden md:inline">·</span>}
|
|
98
|
+
{tagline && <p>{tagline}</p>}
|
|
96
99
|
</div>
|
|
97
100
|
</div>
|
|
98
101
|
</footer>
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* trigger on close, Escape closes). Below `sm` the bar drops the language dropdown (the drawer carries the
|
|
10
10
|
* pills), so logo, theme toggle and the 48 px trigger fit a 320 px viewport without horizontal overflow.
|
|
11
11
|
*
|
|
12
|
+
* The theme toggle and the language dropdown are quiet utilities (.hwio-utility: the nav's weight, size
|
|
13
|
+
* and gray, thin icons). The language dropdown, the mega panels and the drawer close each other: one
|
|
14
|
+
* popup at a time (@hubpav/hwio-web-runtime/popup). A CTA with an icon collapses to the icon alone below
|
|
15
|
+
* 2xl; an external CTA's arrow shows only beside its full label.
|
|
16
|
+
*
|
|
12
17
|
* `navDensity="tight"` with `desktopFrom="xl"`: between 1280 and 1535 px, where the container is still
|
|
13
18
|
* 1280 px wide, the nav labels drop from 15 to 14 px and the item padding from 12 to 8 px, so a long nav
|
|
14
19
|
* plus CTAs (SK/PL/CS labels) fits without sideways scroll. From 1536 px the nav is back to default.
|
|
@@ -60,12 +65,12 @@ const linkAttrs = (item: HWioLinkItem) => (item.external ? { target: '_blank', r
|
|
|
60
65
|
{nav.map((item) => (
|
|
61
66
|
<li class:list={[item.current && 'hwio-nav-current']}>
|
|
62
67
|
{item.mega ? (
|
|
63
|
-
<button type="button" class="px-3 text-[15px]
|
|
68
|
+
<button type="button" class="hwio-weight-nav px-3 text-[15px] text-base-content/80 hover:text-[var(--hwio-nav-hover,var(--color-accent))]" data-hwio-mega-trigger={item.mega} aria-expanded="false" aria-haspopup="true" aria-controls={`${id}-mega-${item.mega}`}>
|
|
64
69
|
{item.label}
|
|
65
70
|
<HWioIcon name="chevron-down" size="sm" class="opacity-50 transition-transform duration-200" />
|
|
66
71
|
</button>
|
|
67
72
|
) : (
|
|
68
|
-
<a href={item.href} class="px-3 text-[15px]
|
|
73
|
+
<a href={item.href} class="hwio-weight-nav px-3 text-[15px] text-base-content/80 hover:text-[var(--hwio-nav-hover,var(--color-accent))] aria-[current=page]:text-[var(--hwio-nav-active,var(--color-primary))]" aria-current={item.current ? 'page' : undefined} data-track={item.track?.id} data-track-location={item.track?.location ?? 'header'}>{item.label}</a>
|
|
69
74
|
)}
|
|
70
75
|
</li>
|
|
71
76
|
))}
|
|
@@ -73,13 +78,15 @@ const linkAttrs = (item: HWioLinkItem) => (item.external ? { target: '_blank', r
|
|
|
73
78
|
</nav>
|
|
74
79
|
|
|
75
80
|
<div class="ml-auto flex items-center gap-2">
|
|
76
|
-
{showThemeToggle && <HWioThemeToggle label={labels.theme ?? 'Theme'} />}
|
|
77
|
-
{languages.length > 1 && <HWioLanguageSwitcher items={languages} label={labels.languages ?? 'Language'} class="max-sm:hidden" />}
|
|
81
|
+
{showThemeToggle && <HWioThemeToggle label={labels.theme ?? 'Theme'} class="hwio-utility" />}
|
|
82
|
+
{languages.length > 1 && <HWioLanguageSwitcher items={languages} label={labels.languages ?? 'Language'} class="hwio-utility max-sm:hidden" />}
|
|
78
83
|
<div class={bp.ctas}>
|
|
79
84
|
{ctas.map((cta) => (
|
|
80
|
-
<HWioButton href={cta.href} external={cta.external} variant={CTA_VARIANT[cta.variant ?? 'primary']} size="sm" track={cta.track ?? { id: 'header-cta', location: 'header' }} class={cta.icon ? '2xl:px-4 px-2.5' : ''} aria-label={cta.icon ? cta.label : undefined} title={cta.icon ? cta.label : undefined}>
|
|
85
|
+
<HWioButton href={cta.href} external={cta.external} externalIcon={!cta.icon} variant={CTA_VARIANT[cta.variant ?? 'primary']} size="sm" track={cta.track ?? { id: 'header-cta', location: 'header' }} class={cta.icon ? '2xl:px-4 px-2.5' : ''} aria-label={cta.icon ? cta.label : undefined} title={cta.icon ? cta.label : undefined}>
|
|
81
86
|
{cta.icon && <HWioIcon name={cta.icon as 'bag'} size="sm" />}
|
|
82
87
|
{cta.icon ? <span class="hidden 2xl:inline">{cta.label}</span> : cta.label}
|
|
88
|
+
{/* Collapsed (icon only, below 2xl) the button shows just its icon; the external glyph returns beside the label. */}
|
|
89
|
+
{cta.icon && cta.external && <HWioIcon name="external" size="sm" class="hidden 2xl:block" />}
|
|
83
90
|
</HWioButton>
|
|
84
91
|
))}
|
|
85
92
|
</div>
|
|
@@ -110,7 +117,7 @@ const linkAttrs = (item: HWioLinkItem) => (item.external ? { target: '_blank', r
|
|
|
110
117
|
{/* One grid cell: DaisyUI lays menu items out as a column-flow grid, so the two lines nest in one span. */}
|
|
111
118
|
<span class="flex flex-col items-start gap-0.5">
|
|
112
119
|
<span class="inline-flex items-center gap-1">{child.label}{child.external && <HWioIcon name="external" size="sm" class="opacity-50" />}</span>
|
|
113
|
-
{child.description && <span class="
|
|
120
|
+
{child.description && <span class="hwio-menu-desc">{child.description}</span>}
|
|
114
121
|
</span>
|
|
115
122
|
</a>
|
|
116
123
|
</li>
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
---
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Two-column hero (direction A): kicker, headline with optional accent line, lead, actions, checklist; media on a soft sky glow.
|
|
4
|
+
* `titleSize="long"` sets a long opening statement (more than about eight words) at 36 to 52 px so it reads in three
|
|
5
|
+
* calm lines (.hwio-h1-long, change request CR-7, pending the Brand Guidelines). Up to two key words of the title may
|
|
6
|
+
* carry the figure gradient through the `title` slot (<span class="hwio-grad">, change request CR-8), instead of a key
|
|
7
|
+
* figure on the same page. `enter` eases the text column and then the media in once the font gate has shown the page.
|
|
8
|
+
*/
|
|
3
9
|
import type { HWioCta } from '../types';
|
|
4
10
|
import HWioButton from './HWioButton.astro';
|
|
5
11
|
import HWioIcon from './HWioIcon.astro';
|
|
6
|
-
interface Props { id?: string; eyebrow?: string; title: string; accent?: string; lead?: string; actions?: HWioCta[]; checklist?: string[]; reverse?: boolean; glow?: boolean; class?: string }
|
|
7
|
-
const { id, eyebrow, title, accent, lead, actions = [], checklist = [], reverse = false, glow = true, class: className = '' } = Astro.props;
|
|
12
|
+
interface Props { id?: string; eyebrow?: string; title: string; accent?: string; lead?: string; actions?: HWioCta[]; checklist?: string[]; reverse?: boolean; glow?: boolean; titleSize?: 'default' | 'long'; enter?: boolean; class?: string }
|
|
13
|
+
const { id, eyebrow, title, accent, lead, actions = [], checklist = [], reverse = false, glow = true, titleSize = 'default', enter = false, class: className = '' } = Astro.props;
|
|
14
|
+
const TITLE_SIZE = { default: '', long: 'hwio-h1-long' } as const;
|
|
8
15
|
const VARIANT = { primary: 'primary', secondary: 'secondary', ghost: 'ghost' } as const;
|
|
9
16
|
---
|
|
10
17
|
<section id={id} class:list={['bg-base-100 text-base-content', className]}>
|
|
11
18
|
<div class:list={['container grid grid-cols-1 items-center gap-12 py-16 lg:grid-cols-[minmax(0,7fr)_minmax(0,5fr)] lg:gap-16 lg:py-24', reverse ? 'lg:[&>*:first-child]:order-2' : '']}>
|
|
12
|
-
<div>
|
|
19
|
+
<div class:list={[enter && 'hwio-enter']}>
|
|
13
20
|
{eyebrow && <p class="hwio-kicker mb-5">{eyebrow}</p>}
|
|
14
|
-
<h1><slot name="title"><span class="block">{title}</span>{accent && <span class="block text-accent">{accent}</span>}</slot></h1>
|
|
21
|
+
<h1 class:list={[TITLE_SIZE[titleSize]]}><slot name="title"><span class="block">{title}</span>{accent && <span class="block text-accent">{accent}</span>}</slot></h1>
|
|
15
22
|
{lead && <p class="hwio-lead mt-7 max-w-[37.5rem] text-xl">{lead}</p>}
|
|
16
23
|
{actions.length > 0 && (
|
|
17
24
|
<div class="mt-9 flex flex-wrap gap-3">
|
|
@@ -20,12 +27,12 @@ const VARIANT = { primary: 'primary', secondary: 'secondary', ghost: 'ghost' } a
|
|
|
20
27
|
)}
|
|
21
28
|
{checklist.length > 0 && (
|
|
22
29
|
<ul class="hwio-muted mt-8 flex flex-wrap gap-x-8 gap-y-3 text-[15px]">
|
|
23
|
-
{checklist.map((c) => <li class="inline-flex items-center gap-2"><HWioIcon name="check" size="sm" class="
|
|
30
|
+
{checklist.map((c) => <li class="inline-flex items-center gap-2"><HWioIcon name="check" size="sm" class="hwio-icon" />{c}</li>)}
|
|
24
31
|
</ul>
|
|
25
32
|
)}
|
|
26
33
|
<slot name="extra" />
|
|
27
34
|
</div>
|
|
28
|
-
<div class:list={['relative flex items-center justify-center', glow ? 'hwio-hero-glow aspect-square rounded-3xl' : '']}><slot name="media" /></div>
|
|
35
|
+
<div class:list={['relative flex items-center justify-center', glow ? 'hwio-hero-glow aspect-square rounded-3xl' : '', enter && 'hwio-enter-media']}><slot name="media" /></div>
|
|
29
36
|
</div>
|
|
30
37
|
</section>
|
|
31
38
|
<style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* An isometric scene drawn with the Brand Guidelines' kit (the artwork library's illustrations/isometric,
|
|
4
|
+
* `make isometric`), inlined so CSS and the runtime reach its parts. Isometric drawings explain;
|
|
5
|
+
* photographs prove. Pass the light-theme SVG as a string (`import scene from './scene.svg?raw'`) and a
|
|
6
|
+
* description for screen readers; the scene carries no text of its own.
|
|
7
|
+
*
|
|
8
|
+
* Motion (@hubpav/hwio-web-runtime/iso): in view, the scene builds itself once: `.iso-object` groups
|
|
9
|
+
* settle in drawing order, then each `.iso-trace` draws from pad to pad and the `.iso-radio` links fade
|
|
10
|
+
* in. Without JavaScript or under reduced motion it shows complete. `animate={false}` keeps it still.
|
|
11
|
+
*
|
|
12
|
+
* Dark theme: the kit's -dark-theme.svg is the same drawing with the plate neutrals and the link colours
|
|
13
|
+
* swapped (verified byte for byte on 2026-10-30), so one inlined copy and the swaps below render both.
|
|
14
|
+
* A drawing outside the kit's palette keeps its colours in dark mode.
|
|
15
|
+
*/
|
|
16
|
+
interface Props {
|
|
17
|
+
svg: string;
|
|
18
|
+
label: string;
|
|
19
|
+
animate?: boolean;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
const { svg, label, animate = true, class: className = '' } = Astro.props;
|
|
23
|
+
const escapeAttr = (text: string) => text.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
24
|
+
const markup = svg
|
|
25
|
+
.replace(/^\s*<\?xml[^>]*>\s*/, '')
|
|
26
|
+
.replace(/<svg\b([^>]*?)\sdata-theme="[^"]*"/, '<svg$1')
|
|
27
|
+
.replace(/<svg\b/, `<svg role="img" aria-label="${escapeAttr(label)}"`);
|
|
28
|
+
---
|
|
29
|
+
<div class:list={['hwio-iso-frame', className]} data-hwio-iso={animate ? 'true' : undefined} set:html={markup} />
|
|
30
|
+
<style is:global>
|
|
31
|
+
.hwio-iso-frame svg {
|
|
32
|
+
display: block;
|
|
33
|
+
width: 100%;
|
|
34
|
+
height: auto;
|
|
35
|
+
overflow: visible;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Dark theme: the kit's -dark-theme.svg colours (plate neutrals, link Red and Gray). */
|
|
39
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#dcdcdc'] { fill: #1d1d26; }
|
|
40
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#dcdcdc'] { stroke: #1d1d26; }
|
|
41
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#dddddd'] { fill: #1e1e27; }
|
|
42
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#dddddd'] { stroke: #1e1e27; }
|
|
43
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e0e0e0'] { fill: #1f1f2a; }
|
|
44
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e0e0e0'] { stroke: #1f1f2a; }
|
|
45
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e1e1e1'] { fill: #20202b; }
|
|
46
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e1e1e1'] { stroke: #20202b; }
|
|
47
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e3e3e3'] { fill: #22222d; }
|
|
48
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e3e3e3'] { stroke: #22222d; }
|
|
49
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e4e4e4'] { fill: #23232e; }
|
|
50
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e4e4e4'] { stroke: #23232e; }
|
|
51
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e7e7e7'] { fill: #242431; }
|
|
52
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e7e7e7'] { stroke: #242431; }
|
|
53
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#e8e8e8'] { fill: #252532; }
|
|
54
|
+
[data-theme$='-dark'] .hwio-iso-frame [stroke='#e8e8e8'] { stroke: #252532; }
|
|
55
|
+
[data-theme$='-dark'] .hwio-iso-frame [fill='#f7f7f7'] { fill: #2e2e3a; }
|
|
56
|
+
[data-theme$='-dark'] .hwio-iso-frame .iso-link [fill='#e30427'] { fill: #f43f5e; }
|
|
57
|
+
[data-theme$='-dark'] .hwio-iso-frame .iso-link [stroke='#e30427'] { stroke: #f43f5e; }
|
|
58
|
+
[data-theme$='-dark'] .hwio-iso-frame .iso-link [fill='#6b6a6a'] { fill: #8c8b8b; }
|
|
59
|
+
[data-theme$='-dark'] .hwio-iso-frame .iso-link [stroke='#6b6a6a'] { stroke: #8c8b8b; }
|
|
60
|
+
|
|
61
|
+
/* Build states, set by the runtime only when motion is allowed (no JavaScript: the full scene). */
|
|
62
|
+
.hwio-iso-anim .iso-object {
|
|
63
|
+
opacity: 0;
|
|
64
|
+
transform: translateY(4px);
|
|
65
|
+
transform-box: fill-box;
|
|
66
|
+
}
|
|
67
|
+
.hwio-iso-anim g.iso-radio {
|
|
68
|
+
opacity: 0;
|
|
69
|
+
}
|
|
70
|
+
.hwio-iso-anim .iso-trace:not(.iso-radio) {
|
|
71
|
+
/* pathLength="1" on every trace: 1px is the whole path. */
|
|
72
|
+
stroke-dasharray: 1px;
|
|
73
|
+
stroke-dashoffset: 1px;
|
|
74
|
+
}
|
|
75
|
+
.hwio-iso-anim .iso-pad {
|
|
76
|
+
opacity: 0;
|
|
77
|
+
}
|
|
78
|
+
/* Transitions only on play, so hiding the parts at bind time is instant. */
|
|
79
|
+
.hwio-iso-anim.hwio-iso-play .iso-object { opacity: 1; transform: none; transition: opacity 300ms ease-out, transform 300ms ease-out; }
|
|
80
|
+
.hwio-iso-anim.hwio-iso-play g.iso-radio { opacity: 1; transition: opacity 300ms ease-out; }
|
|
81
|
+
.hwio-iso-anim.hwio-iso-play .iso-trace:not(.iso-radio) { stroke-dashoffset: 0px; transition: stroke-dashoffset 700ms ease-out; }
|
|
82
|
+
.hwio-iso-anim.hwio-iso-play .iso-pad { opacity: 1; transition: opacity 200ms ease-out; }
|
|
83
|
+
@media (prefers-reduced-motion: reduce) {
|
|
84
|
+
.hwio-iso-anim .iso-object,
|
|
85
|
+
.hwio-iso-anim g.iso-radio,
|
|
86
|
+
.hwio-iso-anim .iso-pad { opacity: 1; transform: none; transition: none; }
|
|
87
|
+
.hwio-iso-anim .iso-trace:not(.iso-radio) { stroke-dashoffset: 0px; transition: none; }
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
90
|
+
<script>
|
|
91
|
+
import { hwioIsoRuntime } from '@hubpav/hwio-web-runtime/iso';
|
|
92
|
+
hwioIsoRuntime();
|
|
93
|
+
</script>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* A figure with its label. `gradient` fills the type with the figure gradient: the one key figure of a
|
|
4
|
+
* page (Brand Guidelines chapter 03, 40 px and up; D33, D44 on the dark ground). Every other figure stays
|
|
5
|
+
* Ink, and so does the key figure on a page whose hero H1 carries gradient words (CR-8). Pass numbers
|
|
6
|
+
* with a non-breaking space before units and in thousands ("7\u00a0000+").
|
|
7
|
+
*
|
|
8
|
+
* `count` counts the figure up once when it enters the viewport (800 ms, ease-out) and ends on the exact
|
|
9
|
+
* `value` text; without JavaScript or under reduced motion the value simply shows.
|
|
10
|
+
*/
|
|
11
|
+
interface Props {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
gradient?: boolean;
|
|
15
|
+
/** md 40 px; lg 40 / 48 px; xl 48 / 72 px (from lg). */
|
|
16
|
+
size?: 'md' | 'lg' | 'xl';
|
|
17
|
+
/** Count-up: to, from (default 0), template around the number ("{n}+", "Since {n}"), thousands separator. */
|
|
18
|
+
count?: { to: number; from?: number; template?: string; group?: string };
|
|
19
|
+
class?: string;
|
|
20
|
+
}
|
|
21
|
+
const { value, label, gradient = false, size = 'lg', count, class: className = '' } = Astro.props;
|
|
22
|
+
const SIZE = {
|
|
23
|
+
md: 'text-[2.5rem]',
|
|
24
|
+
lg: 'text-[2.5rem] lg:text-[3rem]',
|
|
25
|
+
xl: 'text-[3rem] lg:text-[4.5rem]',
|
|
26
|
+
} as const;
|
|
27
|
+
const countAttrs = count
|
|
28
|
+
? {
|
|
29
|
+
'data-hwio-count': String(count.to),
|
|
30
|
+
'data-hwio-count-from': count.from === undefined ? undefined : String(count.from),
|
|
31
|
+
'data-hwio-count-template': count.template,
|
|
32
|
+
'data-hwio-count-group': count.group,
|
|
33
|
+
}
|
|
34
|
+
: {};
|
|
35
|
+
---
|
|
36
|
+
<div class:list={['min-w-0', className]}>
|
|
37
|
+
<p class:list={['font-bold leading-[1.2] tracking-[-0.02em] tabular-nums text-balance', SIZE[size]]}>
|
|
38
|
+
<span class:list={[gradient && 'hwio-key-figure']} {...countAttrs}>{value}</span>
|
|
39
|
+
</p>
|
|
40
|
+
<p class="hwio-muted mt-2 max-w-[22rem] text-[0.9375rem] leading-[1.45]">{label}</p>
|
|
41
|
+
</div>
|
|
42
|
+
<script>
|
|
43
|
+
import { hwioCounterRuntime } from '@hubpav/hwio-web-runtime/counter';
|
|
44
|
+
hwioCounterRuntime();
|
|
45
|
+
</script>
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
+
/**
|
|
3
|
+
* Language switcher: `pills` (a joined row of language codes, for the drawer) or `dropdown` (a
|
|
4
|
+
* `<details>` menu). The dropdown closes on a click outside, on Escape (focus back on its summary),
|
|
5
|
+
* when focus leaves it and when another popup opens (a mega panel, the drawer, another dropdown);
|
|
6
|
+
* opening it closes an open mega panel (@hubpav/hwio-web-runtime/dropdown).
|
|
7
|
+
*/
|
|
2
8
|
import HWioIcon from './HWioIcon.astro';
|
|
3
9
|
interface Item { code: string; label: string; href: string; current?: boolean }
|
|
4
10
|
interface Props { items: Item[]; label: string; variant?: 'pills' | 'dropdown'; class?: string }
|
|
@@ -12,7 +18,7 @@ const current = items.find((i) => i.current);
|
|
|
12
18
|
))}
|
|
13
19
|
</nav>
|
|
14
20
|
) : (
|
|
15
|
-
<details class:list={['dropdown dropdown-end', className]}>
|
|
21
|
+
<details class:list={['dropdown dropdown-end', className]} data-hwio-dropdown>
|
|
16
22
|
<summary class="btn btn-ghost btn-sm gap-1" title={label}>
|
|
17
23
|
<HWioIcon name="globe" size="sm" />
|
|
18
24
|
{/* The accessible name contains the visible code (WCAG 2.5.3), so the label is visually hidden text, not aria-label. */}
|
|
@@ -27,3 +33,7 @@ const current = items.find((i) => i.current);
|
|
|
27
33
|
</ul>
|
|
28
34
|
</details>
|
|
29
35
|
)}
|
|
36
|
+
<script>
|
|
37
|
+
import { hwioDropdownRuntime } from '@hubpav/hwio-web-runtime/dropdown';
|
|
38
|
+
hwioDropdownRuntime();
|
|
39
|
+
</script>
|
|
@@ -10,7 +10,7 @@ const groups = marquee ? [0, 1, 2, 3] : [0];
|
|
|
10
10
|
const SIZE = { xs: 'h-6', sm: 'h-7', md: 'h-7', lg: 'h-10', xl: 'h-12' } as const;
|
|
11
11
|
---
|
|
12
12
|
<section id={id} class:list={['overflow-hidden border-y border-base-300 py-10', TONE[tone], className]}>
|
|
13
|
-
{label && <div class="container"><p class="mb-8 text-center text-sm
|
|
13
|
+
{label && <div class="container"><p class="hwio-weight-label mb-8 text-center text-sm opacity-70">{label}</p></div>}
|
|
14
14
|
<div class="relative">
|
|
15
15
|
{marquee && <div class:list={['pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r to-transparent md:w-32', FADE[tone]]}></div>}
|
|
16
16
|
{marquee && <div class:list={['pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l to-transparent md:w-32', FADE[tone]]}></div>}
|