Pre Release New Changes

This commit is contained in:
2026-07-31 16:30:13 +05:30
parent 358a520bc5
commit 3e565e8d03
189 changed files with 36727 additions and 17249 deletions
+563 -78
View File
@@ -1,5 +1,7 @@
component AnnouncementBar {
props {
@event dismiss = function
badge = ""
badgeIcon = ""
message = "Announcement"
@@ -13,6 +15,7 @@ component AnnouncementBar {
sticky = false
compact = false
size = "default"
width = "default"
color = "primary"
variant = "soft"
role = "status"
@@ -24,108 +27,590 @@ component AnnouncementBar {
view {
<aside
{...attrs}
data-ui-component="AnnouncementBar"
data-size='{size}'
data-width='{width}'
data-color='{color}'
data-variant='{variant}'
data-compact='{compact}'
data-sticky='{sticky}'
data-show='{!dismissed}'
aria-hidden='{dismissed ? "true" : "false"}'
role='{role}'
aria-live='{live}'
data-show='!dismissed'
class='z-40 w-full border-y border-[var(--wire-color-border)] {class}'
class:sticky='sticky'
class:top-0='sticky'
class:bg-[var(--wire-color-primary-soft)]='variant === "soft" && color === "primary"'
class:border-[var(--wire-color-primary-muted)]='variant === "soft" && color === "primary"'
class:bg-[var(--wire-color-info-soft)]='variant === "soft" && color === "info"'
class:border-[var(--wire-color-info-muted)]='variant === "soft" && color === "info"'
class:bg-[var(--wire-color-success-soft)]='variant === "soft" && color === "success"'
class:border-[var(--wire-color-success-muted)]='variant === "soft" && color === "success"'
class:bg-[var(--wire-color-warning-soft)]='variant === "soft" && color === "warning"'
class:border-[var(--wire-color-warning-muted)]='variant === "soft" && color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='variant === "soft" && color === "danger"'
class:border-[var(--wire-color-danger-muted)]='variant === "soft" && color === "danger"'
class:bg-[var(--wire-color-primary)]='variant === "solid" && color === "primary"'
class:text-[var(--wire-color-on-primary)]='variant === "solid" && color === "primary"'
class:bg-[var(--wire-color-danger)]='variant === "solid" && color === "danger"'
class:text-[var(--wire-color-on-danger)]='variant === "solid" && color === "danger"'
class:bg-[var(--wire-color-surface-raised)]='variant === "default"'
class='wire-announcement {class}'
>
<Container columns="1" maxWidth="xl" size="default">
<div
class="flex flex-col gap-4 py-4 sm:flex-row sm:items-center sm:justify-between"
class:py-2.5='compact'
>
<div class="flex min-w-0 items-start gap-3 sm:items-center">
<div class="wire-announcement__surface">
<span
class="wire-announcement__accent"
aria-hidden="true"
></span>
<span
class="wire-announcement__glow wire-announcement__glow--one"
aria-hidden="true"
></span>
<span
class="wire-announcement__glow wire-announcement__glow--two"
aria-hidden="true"
></span>
<div class="wire-announcement__inner">
<div class="wire-announcement__content">
{#if icon}
<span
class="flex size-10 shrink-0 items-center justify-center rounded-xl bg-[var(--wire-color-surface-raised)] text-[var(--wire-color-primary)] shadow-sm"
class:size-8='compact'
class:text-[var(--wire-color-danger)]='color === "danger"'
class:text-[var(--wire-color-warning-text)]='color === "warning"'
class:text-[var(--wire-color-success)]='color === "success"'
class:text-[var(--wire-color-info)]='color === "info"'
>
<span class='{icon + " size-5"}' aria-hidden="true"></span>
<span class="wire-announcement__icon">
<span
class='{icon}'
aria-hidden="true"
></span>
</span>
{/if}
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<div class="wire-announcement__copy">
<div class="wire-announcement__headline">
{#if badge}
<Badge
label='{badge}'
icon='{badgeIcon}'
size="sm"
color='{color}'
variant='{variant === "solid" ? "outline" : "solid"}'
/>
<span class="wire-announcement__badge">
{#if badgeIcon}
<span
class='{badgeIcon}'
aria-hidden="true"
></span>
{/if}
<span>{badge}</span>
</span>
{/if}
<p
class="font-semibold"
class:text-sm='compact || size === "sm"'
class:text-base='!compact && size !== "sm"'
class:text-[var(--wire-color-text)]='variant !== "solid"'
>
<p class="wire-announcement__message">
{message}
</p>
</div>
{#if description && !compact}
<p
class="mt-1 text-sm leading-6"
class:text-[var(--wire-color-text-muted)]='variant !== "solid"'
class:opacity-85='variant === "solid"'
>
<p class="wire-announcement__description">
{description}
</p>
{/if}
</div>
</div>
<div class="flex shrink-0 items-center gap-2 sm:justify-end">
{#if actionLabel}
<TextLink
label='{actionLabel}'
href='{actionHref}'
icon='{actionIcon}'
iconPosition="start"
showArrow="true"
color='{variant === "solid" ? "neutral" : color}'
variant='{variant === "solid" ? "button" : "default"}'
/>
{/if}
{#if actionLabel || dismissible}
<div class="wire-announcement__actions">
{#if actionLabel}
<a
href='{actionHref || "#"}'
class="wire-announcement__action"
>
<span>{actionLabel}</span>
{#if dismissible}
<button
type="button"
aria-label='{dismissLabel}'
class="inline-flex size-9 items-center justify-center rounded-lg text-[var(--wire-color-text-muted)] transition hover:bg-[var(--wire-color-surface-raised)] hover:text-[var(--wire-color-text)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
@click='dismissed = true; event.currentTarget.dispatchEvent(new CustomEvent("dismiss", { bubbles: true }))'
>
<span class="icon-[lucide--x] size-4" aria-hidden="true"></span>
</button>
{/if}
</div>
{#if actionIcon}
<span
class='{actionIcon}'
aria-hidden="true"
></span>
{:else}
<span
class="icon-[lucide--arrow-right]"
aria-hidden="true"
></span>
{/if}
</a>
{/if}
{#if dismissible}
<button
type="button"
aria-label='{dismissLabel}'
title='{dismissLabel}'
class="wire-announcement__dismiss"
@click='dismissed = true; event.currentTarget.dispatchEvent(new CustomEvent("dismiss", { bubbles: true, detail: { message: message } }))'
>
<span
class="icon-[lucide--x]"
aria-hidden="true"
></span>
</button>
{/if}
</div>
{/if}
</div>
</Container>
</div>
</aside>
}
style {
.wire-announcement {
--announcement-accent: var(--wire-color-primary);
--announcement-contrast: var(--wire-color-primary-contrast, #ffffff);
--announcement-soft: var(--wire-color-primary-soft);
--announcement-muted: var(--wire-color-primary-muted);
position: relative;
z-index: 40;
display: block;
width: 100vw;
max-width: 100vw;
margin-inline: calc(50% - 50vw);
color: var(--wire-color-text);
}
.wire-announcement__surface {
width: min(calc(100% - 2rem), 72rem);
margin-inline: auto;
}
.wire-announcement[data-width="wide"] .wire-announcement__surface {
width: min(calc(100% - 2rem), 90rem);
}
.wire-announcement[data-width="full"] .wire-announcement__surface {
width: 100%;
max-width: none;
margin-inline: 0;
border-inline: 0;
border-radius: 0;
}
.wire-announcement[data-sticky="true"] {
position: sticky;
top: 0;
}
.wire-announcement[data-color="secondary"] {
--announcement-accent: var(--wire-color-secondary);
--announcement-contrast: var(--wire-color-secondary-contrast, #ffffff);
--announcement-soft: var(--wire-color-secondary-soft);
--announcement-muted: var(--wire-color-secondary-muted);
}
.wire-announcement[data-color="info"] {
--announcement-accent: var(--wire-color-info);
--announcement-contrast: var(--wire-color-info-contrast, #ffffff);
--announcement-soft: var(--wire-color-info-soft);
--announcement-muted: var(--wire-color-info-muted);
}
.wire-announcement[data-color="success"] {
--announcement-accent: var(--wire-color-success);
--announcement-contrast: var(--wire-color-success-contrast, #ffffff);
--announcement-soft: var(--wire-color-success-soft);
--announcement-muted: var(--wire-color-success-muted);
}
.wire-announcement[data-color="warning"] {
--announcement-accent: var(--wire-color-warning);
--announcement-contrast: var(--wire-color-warning-contrast, #111827);
--announcement-soft: var(--wire-color-warning-soft);
--announcement-muted: var(--wire-color-warning-muted);
}
.wire-announcement[data-color="danger"] {
--announcement-accent: var(--wire-color-danger);
--announcement-contrast: var(--wire-color-danger-contrast, #ffffff);
--announcement-soft: var(--wire-color-danger-soft);
--announcement-muted: var(--wire-color-danger-muted);
}
.wire-announcement__surface {
position: relative;
isolation: isolate;
overflow: hidden;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 1.25rem;
box-shadow:
0 1px 2px rgb(0 0 0 / 0.04),
0 12px 32px rgb(0 0 0 / 0.08);
}
.wire-announcement[data-variant="soft"] .wire-announcement__surface {
background:
linear-gradient(
135deg,
color-mix(in srgb, var(--announcement-soft) 86%, var(--wire-color-surface-raised)),
var(--wire-color-surface-raised)
);
border-color: color-mix(in srgb, var(--announcement-accent) 28%, var(--wire-color-border));
}
.wire-announcement[data-variant="outline"] .wire-announcement__surface {
background: transparent;
border-color: color-mix(in srgb, var(--announcement-accent) 46%, var(--wire-color-border));
box-shadow: none;
}
.wire-announcement[data-variant="minimal"] .wire-announcement__surface {
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-announcement[data-variant="solid"] {
color: var(--announcement-contrast);
}
.wire-announcement[data-variant="solid"] .wire-announcement__surface {
background:
radial-gradient(
circle at 8% 15%,
color-mix(in srgb, var(--announcement-contrast) 14%, transparent),
transparent 30%
),
linear-gradient(
135deg,
color-mix(in srgb, var(--announcement-accent) 96%, white 4%),
color-mix(in srgb, var(--announcement-accent) 76%, black 24%)
);
border-color: color-mix(in srgb, var(--announcement-contrast) 28%, transparent);
box-shadow:
0 1px 0 color-mix(in srgb, var(--announcement-contrast) 20%, transparent) inset,
0 18px 46px color-mix(in srgb, var(--announcement-accent) 30%, transparent);
}
.wire-announcement__accent {
position: absolute;
z-index: -1;
inset-block: 0;
inset-inline-start: 0;
width: 0.25rem;
background: var(--announcement-accent);
}
.wire-announcement[data-variant="solid"] .wire-announcement__accent,
.wire-announcement[data-variant="minimal"] .wire-announcement__accent {
display: none;
}
.wire-announcement__glow {
position: absolute;
z-index: -1;
display: block;
border-radius: 999px;
pointer-events: none;
filter: blur(3rem);
opacity: 0.28;
}
.wire-announcement__glow--one {
width: 16rem;
height: 16rem;
inset-block-start: -10rem;
inset-inline-end: -4rem;
background: var(--announcement-muted);
}
.wire-announcement__glow--two {
width: 10rem;
height: 10rem;
inset-block-end: -7rem;
inset-inline-start: 28%;
background: var(--announcement-soft);
opacity: 0.18;
}
.wire-announcement[data-variant="solid"] .wire-announcement__glow {
background: var(--announcement-contrast);
opacity: 0.1;
}
.wire-announcement[data-variant="minimal"] .wire-announcement__glow {
display: none;
}
.wire-announcement__inner {
position: relative;
display: flex;
flex-direction: column;
gap: 1rem;
width: 100%;
padding: 1rem 1.125rem;
}
.wire-announcement[data-size="sm"] .wire-announcement__inner,
.wire-announcement[data-compact="true"] .wire-announcement__inner {
padding-block: 0.75rem;
}
.wire-announcement[data-size="lg"] .wire-announcement__inner {
padding: 1.25rem 1.5rem;
}
.wire-announcement__content {
display: flex;
align-items: flex-start;
gap: 0.875rem;
min-width: 0;
}
.wire-announcement__icon {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
width: 2.75rem;
height: 2.75rem;
color: var(--announcement-accent);
background: var(--announcement-soft);
border: 1px solid color-mix(in srgb, var(--announcement-accent) 20%, transparent);
border-radius: 0.875rem;
box-shadow: 0 8px 20px color-mix(in srgb, var(--announcement-accent) 12%, transparent);
}
.wire-announcement__icon > span {
width: 1.2rem;
height: 1.2rem;
}
.wire-announcement[data-size="sm"] .wire-announcement__icon,
.wire-announcement[data-compact="true"] .wire-announcement__icon {
width: 2.35rem;
height: 2.35rem;
border-radius: 0.75rem;
}
.wire-announcement[data-size="lg"] .wire-announcement__icon {
width: 3.15rem;
height: 3.15rem;
}
.wire-announcement[data-variant="solid"] .wire-announcement__icon {
color: var(--announcement-contrast);
background: color-mix(in srgb, var(--announcement-contrast) 12%, transparent);
border-color: color-mix(in srgb, var(--announcement-contrast) 22%, transparent);
box-shadow: none;
}
.wire-announcement__copy {
flex: 1 1 auto;
min-width: 0;
padding-top: 0.1rem;
}
.wire-announcement__headline {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.55rem 0.8rem;
}
.wire-announcement__badge {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
gap: 0.35rem;
min-height: 1.6rem;
padding: 0.25rem 0.65rem;
color: var(--announcement-accent);
font-size: 0.6875rem;
font-weight: 700;
line-height: 1;
white-space: nowrap;
background: var(--announcement-soft);
border: 1px solid color-mix(in srgb, var(--announcement-accent) 22%, transparent);
border-radius: 999px;
}
.wire-announcement__badge > span:first-child:not(:last-child) {
width: 0.8rem;
height: 0.8rem;
}
.wire-announcement[data-variant="solid"] .wire-announcement__badge {
color: var(--announcement-contrast);
background: color-mix(in srgb, var(--announcement-contrast) 13%, transparent);
border-color: color-mix(in srgb, var(--announcement-contrast) 24%, transparent);
}
.wire-announcement__message {
margin: 0;
color: var(--wire-color-text);
font-size: 0.9375rem;
font-weight: 650;
line-height: 1.5;
}
.wire-announcement[data-size="sm"] .wire-announcement__message,
.wire-announcement[data-compact="true"] .wire-announcement__message {
font-size: 0.875rem;
}
.wire-announcement[data-size="lg"] .wire-announcement__message {
font-size: 1rem;
}
.wire-announcement[data-variant="solid"] .wire-announcement__message {
color: var(--announcement-contrast);
}
.wire-announcement__description {
max-width: 52rem;
margin: 0.28rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.8125rem;
line-height: 1.55;
}
.wire-announcement[data-size="lg"] .wire-announcement__description {
font-size: 0.875rem;
}
.wire-announcement[data-variant="solid"] .wire-announcement__description {
color: color-mix(in srgb, var(--announcement-contrast) 76%, transparent);
}
.wire-announcement__actions {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: 0.55rem;
padding-inline-start: 3.625rem;
}
.wire-announcement__action,
.wire-announcement__dismiss {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.5rem;
color: var(--wire-color-text);
font-size: 0.8125rem;
font-weight: 650;
text-decoration: none;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 0.75rem;
box-shadow: 0 5px 14px rgb(0 0 0 / 0.06);
transition:
transform 160ms ease,
border-color 160ms ease,
background-color 160ms ease,
box-shadow 160ms ease;
}
.wire-announcement__action {
gap: 0.45rem;
padding: 0.55rem 0.9rem;
}
.wire-announcement__action > span:last-child {
width: 0.95rem;
height: 0.95rem;
transition: transform 160ms ease;
}
.wire-announcement__dismiss {
width: 2.5rem;
padding: 0;
cursor: pointer;
}
.wire-announcement__dismiss > span {
width: 1rem;
height: 1rem;
}
.wire-announcement__action:hover,
.wire-announcement__dismiss:hover {
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--announcement-accent) 42%, var(--wire-color-border));
box-shadow: 0 8px 20px rgb(0 0 0 / 0.1);
}
.wire-announcement__action:hover > span:last-child {
transform: translateX(0.16rem);
}
.wire-announcement__action:focus-visible,
.wire-announcement__dismiss:focus-visible {
outline: 2px solid var(--wire-color-focus, var(--announcement-accent));
outline-offset: 3px;
}
.wire-announcement[data-variant="solid"] .wire-announcement__action {
color: var(--announcement-accent);
background: var(--announcement-contrast);
border-color: transparent;
box-shadow: 0 8px 22px color-mix(in srgb, black 18%, transparent);
}
.wire-announcement[data-variant="solid"] .wire-announcement__dismiss {
color: var(--announcement-contrast);
background: color-mix(in srgb, var(--announcement-contrast) 13%, transparent);
border-color: color-mix(in srgb, var(--announcement-contrast) 24%, transparent);
box-shadow: none;
}
.wire-announcement[data-variant="solid"] .wire-announcement__action:hover {
background: color-mix(in srgb, var(--announcement-contrast) 92%, transparent);
}
.wire-announcement[data-variant="solid"] .wire-announcement__dismiss:hover {
background: color-mix(in srgb, var(--announcement-contrast) 20%, transparent);
border-color: color-mix(in srgb, var(--announcement-contrast) 34%, transparent);
}
@media (min-width: 768px) {
.wire-announcement__inner {
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
}
.wire-announcement__content {
flex: 1 1 auto;
}
.wire-announcement__actions {
justify-content: flex-end;
padding-inline-start: 0;
}
}
@media (max-width: 639px) {
.wire-announcement__surface,
.wire-announcement[data-width="wide"] .wire-announcement__surface {
width: calc(100% - 1rem);
}
.wire-announcement[data-width="full"] .wire-announcement__surface {
width: 100%;
border-radius: 0;
}
.wire-announcement__surface {
border-radius: 1rem;
}
.wire-announcement__inner {
padding-inline: 0.9rem;
}
.wire-announcement__actions {
width: 100%;
padding-inline-start: 3.625rem;
}
.wire-announcement__action {
flex: 1 1 auto;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-announcement__action,
.wire-announcement__dismiss,
.wire-announcement__action > span:last-child {
transition: none;
}
.wire-announcement__action:hover,
.wire-announcement__dismiss:hover,
.wire-announcement__action:hover > span:last-child {
transform: none;
}
}
}
}
+298 -30
View File
@@ -8,9 +8,12 @@ component BackToTop {
offset = "md"
behavior = "smooth"
showProgress = false
showLabel = false
alwaysVisible = false
size = "default"
color = "primary"
variant = "solid"
shape = "round"
class = ""
}
@@ -19,51 +22,316 @@ component BackToTop {
view {
<button
{...attrs}
data-ui-component="BackToTop"
type="button"
aria-label='{ariaLabel}'
title='{label}'
data-show='visible'
class='fixed z-50 inline-flex items-center justify-center rounded-full border border-[var(--wire-color-border)] shadow-xl backdrop-blur transition duration-200 hover:-translate-y-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--wire-color-background)] {class}'
class:bottom-4='offset === "sm"'
class:bottom-6='offset === "md"'
class:bottom-8='offset === "lg"'
class:right-4='position === "right" && offset === "sm"'
class:right-6='position === "right" && offset === "md"'
class:right-8='position === "right" && offset === "lg"'
class:left-4='position === "left" && offset === "sm"'
class:left-6='position === "left" && offset === "md"'
class:left-8='position === "left" && offset === "lg"'
class:left-1/2='position === "center"'
class:-translate-x-1/2='position === "center"'
class:size-10='size === "sm"'
class:size-12='size === "default" || size === "md"'
class:size-14='size === "lg"'
class:bg-[var(--wire-color-primary)]='variant === "solid" && color === "primary"'
class:text-[var(--wire-color-on-primary)]='variant === "solid" && color === "primary"'
class:bg-[var(--wire-color-danger)]='variant === "solid" && color === "danger"'
class:text-[var(--wire-color-on-danger)]='variant === "solid" && color === "danger"'
class:bg-[var(--wire-color-surface-raised)]='variant === "outline" || variant === "soft"'
class:text-[var(--wire-color-primary)]='variant === "outline" || variant === "soft"'
data-position='{position}'
data-offset='{offset}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-shape='{shape}'
data-show-label='{showLabel ? "true" : "false"}'
data-progress='{showProgress ? "true" : "false"}'
data-show='{alwaysVisible || visible}'
class='wire-back-to-top {class}'
style='--wire-back-to-top-progress: {progress}%;'
@window:scroll='visible = window.scrollY >= threshold; progress = Math.min(100, Math.round((window.scrollY / Math.max(1, document.documentElement.scrollHeight - window.innerHeight)) * 100))'
@window:scroll='visible = window.scrollY >= threshold; progress = Math.min(100, Math.max(0, Math.round((window.scrollY / Math.max(1, document.documentElement.scrollHeight - window.innerHeight)) * 100)))'
@click='window.scrollTo({ top: 0, behavior: behavior })'
>
{#if showProgress}
<span class="wire-back-to-top-progress absolute inset-0 rounded-full" aria-hidden="true"></span>
<span class="wire-back-to-top__progress" aria-hidden="true"></span>
{/if}
<span class='{icon + " relative z-10 size-5"}' aria-hidden="true"></span>
<span class="sr-only">{label}</span>
<span class="wire-back-to-top__surface">
<span class='wire-back-to-top__icon {icon}' aria-hidden="true"></span>
{#if showLabel}
<span class="wire-back-to-top__label">{label}</span>
{/if}
</span>
<span class="wire-back-to-top__sr">{label}</span>
</button>
}
style {
.wire-back-to-top-progress {
.wire-back-to-top {
--back-accent: var(--wire-color-primary);
--back-accent-hover: var(--wire-color-primary-hover);
--back-accent-soft: var(--wire-color-primary-soft);
--back-contrast: var(--wire-color-primary-contrast);
position: fixed;
z-index: 70;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 3rem;
min-height: 3rem;
padding: 0;
color: var(--back-contrast);
background: transparent;
border: 0;
border-radius: 999px;
cursor: pointer;
filter: drop-shadow(0 14px 30px color-mix(in srgb, black 22%, transparent));
transition:
transform 180ms ease,
opacity 180ms ease,
filter 180ms ease;
}
.wire-back-to-top[data-color="secondary"] {
--back-accent: var(--wire-color-secondary);
--back-accent-hover: var(--wire-color-secondary-hover);
--back-accent-soft: var(--wire-color-secondary-soft);
--back-contrast: var(--wire-color-secondary-contrast);
}
.wire-back-to-top[data-color="info"] {
--back-accent: var(--wire-color-info);
--back-accent-hover: var(--wire-color-info-hover);
--back-accent-soft: var(--wire-color-info-soft);
--back-contrast: var(--wire-color-info-contrast);
}
.wire-back-to-top[data-color="success"] {
--back-accent: var(--wire-color-success);
--back-accent-hover: var(--wire-color-success-hover);
--back-accent-soft: var(--wire-color-success-soft);
--back-contrast: var(--wire-color-success-contrast);
}
.wire-back-to-top[data-color="warning"] {
--back-accent: var(--wire-color-warning);
--back-accent-hover: var(--wire-color-warning-hover);
--back-accent-soft: var(--wire-color-warning-soft);
--back-contrast: var(--wire-color-warning-contrast);
}
.wire-back-to-top[data-color="danger"] {
--back-accent: var(--wire-color-danger);
--back-accent-hover: var(--wire-color-danger-hover);
--back-accent-soft: var(--wire-color-danger-soft);
--back-contrast: var(--wire-color-danger-contrast);
}
.wire-back-to-top[data-position="right"] {
right: 1.5rem;
}
.wire-back-to-top[data-position="left"] {
left: 1.5rem;
}
.wire-back-to-top[data-position="center"] {
left: 50%;
transform: translateX(-50%);
}
.wire-back-to-top[data-position="center"]:hover {
transform: translateX(-50%) translateY(-3px);
}
.wire-back-to-top[data-offset="sm"] {
bottom: 1rem;
}
.wire-back-to-top[data-offset="md"] {
bottom: 1.5rem;
}
.wire-back-to-top[data-offset="lg"] {
bottom: 2rem;
}
.wire-back-to-top[data-offset="xl"] {
bottom: 3rem;
}
.wire-back-to-top[data-size="sm"] {
min-width: 2.5rem;
min-height: 2.5rem;
}
.wire-back-to-top[data-size="lg"] {
min-width: 3.5rem;
min-height: 3.5rem;
}
.wire-back-to-top[data-show-label="true"] {
min-width: 0;
}
.wire-back-to-top:hover {
transform: translateY(-3px);
filter: drop-shadow(0 18px 36px color-mix(in srgb, var(--back-accent) 24%, transparent));
}
.wire-back-to-top:active {
transform: translateY(-1px) scale(0.98);
}
.wire-back-to-top__surface {
position: relative;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-width: 3rem;
min-height: 3rem;
padding: 0.75rem;
color: var(--back-contrast);
background:
linear-gradient(145deg, color-mix(in srgb, white 10%, transparent), transparent 56%),
var(--back-accent);
border: 1px solid color-mix(in srgb, white 18%, transparent);
border-radius: inherit;
box-shadow:
0 1px 0 color-mix(in srgb, white 20%, transparent) inset,
0 10px 24px color-mix(in srgb, black 18%, transparent);
backdrop-filter: blur(12px);
transition:
background-color 160ms ease,
border-color 160ms ease,
color 160ms ease;
}
.wire-back-to-top[data-size="sm"] .wire-back-to-top__surface {
min-width: 2.5rem;
min-height: 2.5rem;
padding: 0.58rem;
}
.wire-back-to-top[data-size="lg"] .wire-back-to-top__surface {
min-width: 3.5rem;
min-height: 3.5rem;
padding: 0.9rem;
}
.wire-back-to-top[data-show-label="true"] .wire-back-to-top__surface {
min-width: 0;
padding-inline: 1rem;
}
.wire-back-to-top[data-variant="outline"] .wire-back-to-top__surface {
color: var(--back-accent);
background: color-mix(in srgb, var(--wire-color-surface-raised) 88%, transparent);
border-color: color-mix(in srgb, var(--back-accent) 46%, var(--wire-color-border));
}
.wire-back-to-top[data-variant="soft"] .wire-back-to-top__surface {
color: var(--back-accent);
background: color-mix(in srgb, var(--back-accent-soft) 88%, var(--wire-color-surface-raised));
border-color: color-mix(in srgb, var(--back-accent) 22%, var(--wire-color-border));
}
.wire-back-to-top[data-variant="ghost"] .wire-back-to-top__surface {
color: var(--back-accent);
background: color-mix(in srgb, var(--wire-color-surface-raised) 68%, transparent);
border-color: transparent;
box-shadow: none;
}
.wire-back-to-top[data-variant="minimal"] .wire-back-to-top__surface {
color: var(--back-accent);
background: transparent;
border-color: transparent;
box-shadow: none;
backdrop-filter: none;
}
.wire-back-to-top[data-shape="rounded"] {
border-radius: 0.9rem;
}
.wire-back-to-top[data-shape="pill"] {
border-radius: 999px;
}
.wire-back-to-top__progress {
position: absolute;
inset: -0.22rem;
z-index: 1;
border-radius: inherit;
background: conic-gradient(
currentColor var(--wire-back-to-top-progress),
transparent var(--wire-back-to-top-progress)
var(--back-accent) var(--wire-back-to-top-progress),
color-mix(in srgb, var(--wire-color-border) 72%, transparent) var(--wire-back-to-top-progress)
);
opacity: 0.22;
mask: radial-gradient(farthest-side, transparent calc(100% - 0.18rem), #000 calc(100% - 0.17rem));
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 0.18rem), #000 calc(100% - 0.17rem));
pointer-events: none;
}
.wire-back-to-top__icon {
width: 1.1rem;
height: 1.1rem;
flex: 0 0 auto;
}
.wire-back-to-top[data-size="sm"] .wire-back-to-top__icon {
width: 0.95rem;
height: 0.95rem;
}
.wire-back-to-top[data-size="lg"] .wire-back-to-top__icon {
width: 1.25rem;
height: 1.25rem;
}
.wire-back-to-top__label {
white-space: nowrap;
font-size: 0.78rem;
font-weight: 700;
line-height: 1;
}
.wire-back-to-top__sr {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.wire-back-to-top:focus-visible {
outline: 2px solid var(--wire-color-focus);
outline-offset: 4px;
}
@media (max-width: 639px) {
.wire-back-to-top[data-position="right"] {
right: 1rem;
}
.wire-back-to-top[data-position="left"] {
left: 1rem;
}
.wire-back-to-top[data-offset="lg"],
.wire-back-to-top[data-offset="xl"] {
bottom: 1.25rem;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-back-to-top,
.wire-back-to-top__surface {
transition: none;
}
.wire-back-to-top:hover,
.wire-back-to-top[data-position="center"]:hover {
transform: none;
}
}
}
}
+310 -8
View File
@@ -1,19 +1,321 @@
component Breadcrumb {
props {
@event navigate = function
@event click = function
size = "default"
color = "primary"
@event select = function
label = "Breadcrumb"
items = []
active = ""
orientation = "horizontal"
separator = "chevron"
showHome = false
homeLabel = "Home"
homeHref = "/"
homeIcon = "icon-[lucide--house]"
size = "default"
color = "primary"
variant = "minimal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--breadcrumb wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
<nav
{...attrs}
data-ui-component="Breadcrumb"
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
aria-label='{label}'
class='wire-breadcrumb {class}'
>
<ol class="wire-breadcrumb__list">
{#if showHome}
<li class="wire-breadcrumb__item wire-breadcrumb__item--home">
<a
href='{homeHref || "/"}'
class="wire-breadcrumb__link wire-breadcrumb__home"
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: { label: homeLabel, href: homeHref || "/", value: "home" }, itemIndex: -1 } }))'
>
{#if homeIcon === "icon-[lucide--house]"}
<span class="icon-[lucide--house] wire-breadcrumb__icon" aria-hidden="true"></span>
{:else if homeIcon}
<span class='{homeIcon} wire-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__home-label">{homeLabel}</span>
</a>
</li>
{/if}
{#each items as item, itemIndex}
<li
class="wire-breadcrumb__item"
data-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "true" : "false"}'
>
{#if showHome || itemIndex > 0}
<span class="wire-breadcrumb__separator" aria-hidden="true">
{#if separator === "slash"}
<span>/</span>
{:else if separator === "dot"}
<span>•</span>
{:else if separator === "arrow"}
<span>→</span>
{:else}
<span class="icon-[lucide--chevron-right] wire-breadcrumb__separator-icon"></span>
{/if}
</span>
{/if}
{#if item.href && !item.disabled && !(item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1))}
<a
href='{item.href}'
target='{item.target || ""}'
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
class="wire-breadcrumb__link"
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: item, itemIndex: itemIndex } }))'
>
{#if item.icon}
<span class='{item.icon} wire-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__label">{item.label || item.title}</span>
{#if item.external}
<span class="icon-[lucide--arrow-up-right] wire-breadcrumb__external" aria-hidden="true"></span>
{/if}
</a>
{:else}
<span
class="wire-breadcrumb__current"
aria-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "page" : ""}'
aria-disabled='{item.disabled ? "true" : "false"}'
>
{#if item.icon}
<span class='{item.icon} wire-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__label">{item.label || item.title}</span>
</span>
{/if}
</li>
{/each}
</ol>
</nav>
}
style {
.wire-breadcrumb {
--breadcrumb-accent: var(--wire-color-primary);
--breadcrumb-soft: var(--wire-color-primary-soft);
--breadcrumb-muted: var(--wire-color-primary-muted);
--breadcrumb-text: var(--wire-color-primary-text);
min-width: 0;
color: var(--wire-color-text-muted);
}
.wire-breadcrumb[data-color="secondary"] {
--breadcrumb-accent: var(--wire-color-secondary);
--breadcrumb-soft: var(--wire-color-secondary-soft);
--breadcrumb-muted: var(--wire-color-secondary-muted);
--breadcrumb-text: var(--wire-color-secondary-text);
}
.wire-breadcrumb[data-color="info"] {
--breadcrumb-accent: var(--wire-color-info);
--breadcrumb-soft: var(--wire-color-info-soft);
--breadcrumb-muted: var(--wire-color-info-muted);
--breadcrumb-text: var(--wire-color-info-text);
}
.wire-breadcrumb[data-color="success"] {
--breadcrumb-accent: var(--wire-color-success);
--breadcrumb-soft: var(--wire-color-success-soft);
--breadcrumb-muted: var(--wire-color-success-muted);
--breadcrumb-text: var(--wire-color-success-text);
}
.wire-breadcrumb[data-color="warning"] {
--breadcrumb-accent: var(--wire-color-warning);
--breadcrumb-soft: var(--wire-color-warning-soft);
--breadcrumb-muted: var(--wire-color-warning-muted);
--breadcrumb-text: var(--wire-color-warning-text);
}
.wire-breadcrumb[data-color="danger"] {
--breadcrumb-accent: var(--wire-color-danger);
--breadcrumb-soft: var(--wire-color-danger-soft);
--breadcrumb-muted: var(--wire-color-danger-muted);
--breadcrumb-text: var(--wire-color-danger-text);
}
.wire-breadcrumb[data-variant="soft"] {
width: fit-content;
max-width: 100%;
padding: 0.45rem 0.65rem;
background: var(--breadcrumb-soft);
border: 1px solid var(--breadcrumb-muted);
border-radius: 0.75rem;
}
.wire-breadcrumb[data-variant="outline"] {
width: fit-content;
max-width: 100%;
padding: 0.45rem 0.65rem;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 0.75rem;
box-shadow: var(--wire-shadow-sm);
}
.wire-breadcrumb[data-variant="contrast"] {
color: rgba(255, 255, 255, 0.78);
}
.wire-breadcrumb__list {
display: flex;
align-items: center;
gap: 0;
min-width: 0;
margin: 0;
padding: 0;
overflow-x: auto;
list-style: none;
scrollbar-width: none;
white-space: nowrap;
}
.wire-breadcrumb__list::-webkit-scrollbar {
display: none;
}
.wire-breadcrumb__item {
display: inline-flex;
align-items: center;
min-width: 0;
flex: 0 0 auto;
}
.wire-breadcrumb__separator {
display: inline-flex;
align-items: center;
justify-content: center;
margin-inline: 0.45rem;
color: var(--wire-color-text-subtle);
font-size: 0.75rem;
opacity: 0.72;
}
.wire-breadcrumb__separator-icon {
width: 0.9rem;
height: 0.9rem;
}
.wire-breadcrumb__link,
.wire-breadcrumb__current {
display: inline-flex;
align-items: center;
gap: 0.4rem;
min-width: 0;
min-height: 2rem;
padding: 0.25rem 0.4rem;
border-radius: 0.55rem;
font-size: 0.8125rem;
line-height: 1.25rem;
text-decoration: none;
}
.wire-breadcrumb[data-size="sm"] .wire-breadcrumb__link,
.wire-breadcrumb[data-size="sm"] .wire-breadcrumb__current {
min-height: 1.75rem;
font-size: 0.75rem;
}
.wire-breadcrumb[data-size="lg"] .wire-breadcrumb__link,
.wire-breadcrumb[data-size="lg"] .wire-breadcrumb__current {
min-height: 2.25rem;
font-size: 0.875rem;
}
.wire-breadcrumb__link {
color: var(--wire-color-text-muted);
font-weight: 600;
transition:
color 160ms ease,
background 160ms ease;
}
.wire-breadcrumb__link:hover {
color: var(--breadcrumb-accent);
background: var(--breadcrumb-soft);
}
.wire-breadcrumb__link:focus-visible {
color: var(--breadcrumb-accent);
outline: 2px solid var(--breadcrumb-accent);
outline-offset: 2px;
}
.wire-breadcrumb__current {
max-width: 22rem;
color: var(--wire-color-text);
font-weight: 750;
}
.wire-breadcrumb__item[data-current="true"] .wire-breadcrumb__current {
color: var(--breadcrumb-text);
background: var(--breadcrumb-soft);
}
.wire-breadcrumb__current[aria-disabled="true"] {
opacity: 0.58;
}
.wire-breadcrumb__icon,
.wire-breadcrumb__external {
flex: 0 0 auto;
width: 0.95rem;
height: 0.95rem;
}
.wire-breadcrumb__home {
color: var(--breadcrumb-accent);
}
.wire-breadcrumb__label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__link,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__current,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__home {
color: inherit;
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__link:hover,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__item[data-current="true"] .wire-breadcrumb__current {
color: #ffffff;
background: rgba(255, 255, 255, 0.12);
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__separator {
color: rgba(255, 255, 255, 0.62);
}
@media (max-width: 639px) {
.wire-breadcrumb__home-label {
display: none;
}
.wire-breadcrumb__current {
max-width: 13rem;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-breadcrumb__link {
transition: none;
}
}
}
}
+663 -115
View File
@@ -15,136 +15,684 @@ component CTASection {
secondaryHref = ""
secondaryIcon = ""
backgroundImage = ""
visualImage = ""
visualAlt = ""
visualIcon = ""
visualTitle = ""
visualDescription = ""
visualItems = []
visualPosition = "right"
maxWidth = "xl"
fullBleed = false
class = ""
}
view {
<Section
spacing='{size === "compact" ? "md" : "lg"}'
variant="default"
color='{color}'
maxWidth='{maxWidth}'
class='{class}'
<section
{...attrs}
data-ui-component="CTASection"
data-align='{align}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-max-width='{maxWidth}'
data-full-bleed='{fullBleed ? "true" : "false"}'
data-visual-position='{visualPosition}'
class='wire-cta-section {class}'
>
<div
data-ui-component="CTASection"
class="relative isolate overflow-hidden rounded-3xl border border-[var(--wire-color-border)] p-8 shadow-xl sm:p-10 lg:p-14"
class:bg-[var(--wire-color-primary)]='variant === "solid" && color === "primary"'
class:text-[var(--wire-color-on-primary)]='variant === "solid" && color === "primary"'
class:bg-[var(--wire-color-danger)]='variant === "solid" && color === "danger"'
class:text-[var(--wire-color-on-danger)]='variant === "solid" && color === "danger"'
class:bg-[var(--wire-color-primary-soft)]='variant === "soft" && color === "primary"'
class:bg-[var(--wire-color-success-soft)]='variant === "soft" && color === "success"'
class:bg-[var(--wire-color-warning-soft)]='variant === "soft" && color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='variant === "soft" && color === "danger"'
class:bg-[var(--wire-color-surface-raised)]='variant === "default" || variant === "outline"'
class:bg-gradient-to-br='variant === "gradient"'
class:from-[var(--wire-color-primary)]='variant === "gradient"'
class:to-[var(--wire-color-secondary)]='variant === "gradient"'
class:text-[var(--wire-color-on-primary)]='variant === "gradient"'
class:text-center='align === "center"'
>
{#if backgroundImage}
<img
src='{backgroundImage}'
alt=""
aria-hidden="true"
class="absolute inset-0 -z-20 h-full w-full object-cover opacity-15"
/>
{/if}
<div class="wire-cta-section__outer">
<div class="wire-cta-section__surface">
{#if backgroundImage}
<img
src='{backgroundImage}'
alt=""
aria-hidden="true"
class="wire-cta-section__background-image"
/>
{/if}
<div
class="pointer-events-none absolute -right-20 -top-20 -z-10 size-72 rounded-full bg-white/15 blur-3xl"
aria-hidden="true"
></div>
<div
class="pointer-events-none absolute -bottom-24 -left-20 -z-10 size-72 rounded-full bg-black/10 blur-3xl"
aria-hidden="true"
></div>
<div class="wire-cta-section__glow wire-cta-section__glow--one" aria-hidden="true"></div>
<div class="wire-cta-section__glow wire-cta-section__glow--two" aria-hidden="true"></div>
<div
class="flex flex-col gap-8"
class:items-center='align === "center"'
class:lg:flex-row='align === "split"'
class:lg:items-center='align === "split"'
class:lg:justify-between='align === "split"'
>
<div class="max-w-3xl">
{#if icon}
<span
class="mb-5 inline-flex size-12 items-center justify-center rounded-2xl bg-white/15"
class:bg-[var(--wire-color-primary-soft)]='variant !== "solid" && variant !== "gradient"'
class:text-[var(--wire-color-primary)]='variant !== "solid" && variant !== "gradient"'
>
<span class='{icon + " size-6"}' aria-hidden="true"></span>
</span>
{/if}
<div class="wire-cta-section__layout">
<div class="wire-cta-section__content">
{#if icon}
<div class="wire-cta-section__icon" aria-hidden="true">
<span class='{icon}'></span>
</div>
{/if}
{#if eyebrow}
<p
class="text-xs font-bold uppercase tracking-[0.18em]"
class:text-[var(--wire-color-primary)]='variant !== "solid" && variant !== "gradient"'
class:opacity-80='variant === "solid" || variant === "gradient"'
>
{eyebrow}
</p>
{/if}
{#if eyebrow}
<p class="wire-cta-section__eyebrow">{eyebrow}</p>
{/if}
<h2
class="mt-3 text-3xl font-bold leading-tight tracking-tight sm:text-4xl lg:text-5xl"
class:text-[var(--wire-color-text)]='variant !== "solid" && variant !== "gradient"'
>
{title}
</h2>
<h2 class="wire-cta-section__title">{title}</h2>
{#if description}
<p
class="mt-4 text-base leading-7 sm:text-lg"
class:text-[var(--wire-color-text-muted)]='variant !== "solid" && variant !== "gradient"'
class:opacity-85='variant === "solid" || variant === "gradient"'
>
{description}
</p>
{/if}
{#if description}
<p class="wire-cta-section__description">{description}</p>
{/if}
<slot></slot>
<slot></slot>
{#if primaryLabel || secondaryLabel}
<div class="wire-cta-section__actions">
{#if primaryLabel}
<a href='{primaryHref || "#"}' class="wire-cta-section__action wire-cta-section__action--primary">
{#if primaryIcon}
<span class='wire-cta-section__action-icon {primaryIcon}' aria-hidden="true"></span>
{/if}
<span>{primaryLabel}</span>
</a>
{/if}
{#if secondaryLabel}
<a href='{secondaryHref || "#"}' class="wire-cta-section__action wire-cta-section__action--secondary">
{#if secondaryIcon}
<span class='wire-cta-section__action-icon {secondaryIcon}' aria-hidden="true"></span>
{/if}
<span>{secondaryLabel}</span>
</a>
{/if}
<slot name="actions"></slot>
</div>
{:else}
<div class="wire-cta-section__actions wire-cta-section__actions--slot">
<slot name="actions"></slot>
</div>
{/if}
</div>
<div class="wire-cta-section__visual-column">
<slot name="visual"></slot>
{#if visualImage || visualIcon || visualTitle || visualDescription || visualItems.length > 0}
<div class="wire-cta-section__visual">
{#if visualImage}
<div class="wire-cta-section__visual-media">
<img src='{visualImage}' alt='{visualAlt}' class="wire-cta-section__visual-image" />
</div>
{/if}
{#if visualIcon || visualTitle || visualDescription}
<div class="wire-cta-section__visual-header">
{#if visualIcon}
<div class="wire-cta-section__visual-icon" aria-hidden="true">
<span class='{visualIcon}'></span>
</div>
{/if}
<div class="wire-cta-section__visual-copy">
{#if visualTitle}
<h3 class="wire-cta-section__visual-title">{visualTitle}</h3>
{/if}
{#if visualDescription}
<p class="wire-cta-section__visual-description">{visualDescription}</p>
{/if}
</div>
</div>
{/if}
{#if visualItems.length > 0}
<div class="wire-cta-section__visual-list">
{#each visualItems as item}
<div class="wire-cta-section__visual-item">
{#if item.icon}
<span class='wire-cta-section__visual-item-icon {item.icon}' aria-hidden="true"></span>
{/if}
<div>
<strong>{item.label || item.title}</strong>
{#if item.description}
<p>{item.description}</p>
{/if}
</div>
</div>
{/each}
</div>
{/if}
</div>
{/if}
</div>
</div>
<HeroActions
actions='{[
{
label: primaryLabel,
href: primaryHref,
icon: primaryIcon,
variant: "default",
color: color,
controlClass: variant === "solid" || variant === "gradient" ? "!bg-white !text-[var(--wire-color-primary)] hover:!bg-white/90" : ""
},
{
label: secondaryLabel,
href: secondaryHref,
icon: secondaryIcon,
variant: "outline",
color: color,
controlClass: variant === "solid" || variant === "gradient" ? "!border-white/50 !text-white hover:!bg-white/10" : ""
}
]}'
align='{align === "center" ? "center" : "right"}'
stackOnMobile="true"
fullWidthMobile="true"
size="lg"
color='{color}'
class="shrink-0"
>
<slot name="actions"></slot>
</HeroActions>
<slot name="visual"></slot>
<div class="wire-cta-section__footer">
<slot name="footer"></slot>
</div>
</div>
<slot name="footer"></slot>
</div>
</Section>
</section>
}
style {
.wire-cta-section {
--cta-accent: var(--wire-color-primary);
--cta-accent-hover: var(--wire-color-primary-hover);
--cta-accent-soft: var(--wire-color-primary-soft);
--cta-accent-muted: var(--wire-color-primary-muted);
--cta-contrast: var(--wire-color-primary-contrast);
--cta-border: color-mix(in srgb, var(--cta-accent) 15%, var(--wire-color-border));
position: relative;
width: 100%;
color: var(--wire-color-text);
}
.wire-cta-section[data-color="secondary"] {
--cta-accent: var(--wire-color-secondary);
--cta-accent-hover: var(--wire-color-secondary-hover);
--cta-accent-soft: var(--wire-color-secondary-soft);
--cta-accent-muted: var(--wire-color-secondary-muted);
--cta-contrast: var(--wire-color-secondary-contrast);
}
.wire-cta-section[data-color="info"] {
--cta-accent: var(--wire-color-info);
--cta-accent-hover: var(--wire-color-info-hover);
--cta-accent-soft: var(--wire-color-info-soft);
--cta-accent-muted: var(--wire-color-info-muted);
--cta-contrast: var(--wire-color-info-contrast);
}
.wire-cta-section[data-color="success"] {
--cta-accent: var(--wire-color-success);
--cta-accent-hover: var(--wire-color-success-hover);
--cta-accent-soft: var(--wire-color-success-soft);
--cta-accent-muted: var(--wire-color-success-muted);
--cta-contrast: var(--wire-color-success-contrast);
}
.wire-cta-section[data-color="warning"] {
--cta-accent: var(--wire-color-warning);
--cta-accent-hover: var(--wire-color-warning-hover);
--cta-accent-soft: var(--wire-color-warning-soft);
--cta-accent-muted: var(--wire-color-warning-muted);
--cta-contrast: var(--wire-color-warning-contrast);
}
.wire-cta-section[data-color="danger"] {
--cta-accent: var(--wire-color-danger);
--cta-accent-hover: var(--wire-color-danger-hover);
--cta-accent-soft: var(--wire-color-danger-soft);
--cta-accent-muted: var(--wire-color-danger-muted);
--cta-contrast: var(--wire-color-danger-contrast);
}
.wire-cta-section__outer {
width: min(calc(100% - 2rem), 80rem);
margin-inline: auto;
}
.wire-cta-section[data-max-width="compact"] .wire-cta-section__outer {
width: min(calc(100% - 2rem), 64rem);
}
.wire-cta-section[data-max-width="lg"] .wire-cta-section__outer {
width: min(calc(100% - 2rem), 72rem);
}
.wire-cta-section[data-max-width="wide"] .wire-cta-section__outer,
.wire-cta-section[data-max-width="2xl"] .wire-cta-section__outer {
width: min(calc(100% - 2rem), 90rem);
}
.wire-cta-section[data-max-width="full"] .wire-cta-section__outer {
width: 100%;
max-width: none;
}
.wire-cta-section[data-full-bleed="true"] .wire-cta-section__outer {
width: 100%;
max-width: none;
}
.wire-cta-section__surface {
position: relative;
isolation: isolate;
overflow: hidden;
padding: 3rem;
background:
radial-gradient(circle at 92% 12%, color-mix(in srgb, var(--cta-accent) 10%, transparent), transparent 28%),
var(--wire-color-surface-raised);
border: 1px solid var(--cta-border);
border-radius: 1.5rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 70px color-mix(in srgb, black 15%, transparent);
}
.wire-cta-section[data-size="compact"] .wire-cta-section__surface,
.wire-cta-section[data-size="sm"] .wire-cta-section__surface {
padding: 2rem;
}
.wire-cta-section[data-size="lg"] .wire-cta-section__surface {
padding: 4rem;
}
.wire-cta-section[data-full-bleed="true"] .wire-cta-section__surface {
border-inline: 0;
border-radius: 0;
}
.wire-cta-section[data-variant="soft"] .wire-cta-section__surface {
background:
radial-gradient(circle at 92% 12%, color-mix(in srgb, var(--cta-accent) 14%, transparent), transparent 30%),
linear-gradient(135deg, var(--cta-accent-soft), transparent 64%),
var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-cta-section[data-variant="outline"] .wire-cta-section__surface {
background: transparent;
box-shadow: none;
}
.wire-cta-section[data-variant="gradient"] .wire-cta-section__surface {
color: var(--cta-contrast);
background:
radial-gradient(circle at 90% 8%, color-mix(in srgb, white 16%, transparent), transparent 30%),
linear-gradient(135deg, var(--cta-accent), color-mix(in srgb, var(--cta-accent) 70%, var(--wire-color-secondary)));
border-color: color-mix(in srgb, white 22%, transparent);
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__surface {
color: var(--cta-contrast);
background:
radial-gradient(circle at 90% 8%, color-mix(in srgb, white 14%, transparent), transparent 30%),
var(--cta-accent);
border-color: color-mix(in srgb, white 22%, transparent);
box-shadow: 0 30px 80px color-mix(in srgb, var(--cta-accent) 28%, transparent);
}
.wire-cta-section__background-image {
position: absolute;
inset: 0;
z-index: -3;
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.14;
}
.wire-cta-section__glow {
position: absolute;
z-index: -2;
width: 22rem;
height: 22rem;
pointer-events: none;
background: var(--cta-accent);
border-radius: 999px;
filter: blur(110px);
opacity: 0.1;
}
.wire-cta-section__glow--one {
top: -12rem;
right: -7rem;
}
.wire-cta-section__glow--two {
bottom: -14rem;
left: -8rem;
opacity: 0.06;
}
.wire-cta-section__layout {
display: grid;
grid-template-columns: minmax(0, 1fr);
align-items: center;
gap: 2.5rem;
}
.wire-cta-section__content,
.wire-cta-section__visual-column {
min-width: 0;
}
.wire-cta-section__content {
max-width: 48rem;
}
.wire-cta-section[data-align="center"] .wire-cta-section__content {
margin-inline: auto;
text-align: center;
}
.wire-cta-section[data-align="right"] .wire-cta-section__content {
margin-left: auto;
text-align: right;
}
.wire-cta-section__icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
margin-bottom: 1.1rem;
color: var(--cta-accent);
background: var(--cta-accent-soft);
border: 1px solid color-mix(in srgb, var(--cta-accent) 20%, transparent);
border-radius: 0.9rem;
font-size: 1.35rem;
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__icon,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__icon {
color: currentColor;
background: color-mix(in srgb, white 14%, transparent);
border-color: color-mix(in srgb, white 22%, transparent);
}
.wire-cta-section__eyebrow {
margin: 0;
color: var(--cta-accent);
font-size: 0.72rem;
font-weight: 650;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__eyebrow,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__eyebrow {
color: currentColor;
opacity: 0.78;
}
.wire-cta-section__title {
max-width: 18ch;
margin: 0.75rem 0 0;
color: var(--wire-color-text);
font-size: clamp(2rem, 5vw, 4rem);
font-weight: 650;
line-height: 1.05;
letter-spacing: -0.045em;
}
.wire-cta-section[data-align="center"] .wire-cta-section__title,
.wire-cta-section[data-align="right"] .wire-cta-section__title {
margin-inline: auto;
}
.wire-cta-section[data-align="right"] .wire-cta-section__title {
margin-right: 0;
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__title,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__title {
color: currentColor;
}
.wire-cta-section__description {
max-width: 44rem;
margin: 1rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.96rem;
line-height: 1.75;
}
.wire-cta-section[data-align="center"] .wire-cta-section__description,
.wire-cta-section[data-align="right"] .wire-cta-section__description {
margin-inline: auto;
}
.wire-cta-section[data-align="right"] .wire-cta-section__description {
margin-right: 0;
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__description,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__description {
color: color-mix(in srgb, currentColor 82%, transparent);
}
.wire-cta-section__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
margin-top: 1.75rem;
}
.wire-cta-section__actions--slot:empty {
display: none;
}
.wire-cta-section[data-align="center"] .wire-cta-section__actions {
justify-content: center;
}
.wire-cta-section[data-align="right"] .wire-cta-section__actions {
justify-content: flex-end;
}
.wire-cta-section__action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-height: 2.85rem;
padding: 0.78rem 1.1rem;
border-radius: 0.75rem;
font-size: 0.84rem;
font-weight: 700;
line-height: 1;
text-decoration: none;
transition:
transform 160ms ease,
background-color 160ms ease,
color 160ms ease,
border-color 160ms ease;
}
.wire-cta-section__action:hover {
transform: translateY(-2px);
}
.wire-cta-section__action--primary {
color: var(--cta-contrast);
background: var(--cta-accent);
border: 1px solid transparent;
}
.wire-cta-section__action--primary:hover {
background: var(--cta-accent-hover);
}
.wire-cta-section__action--secondary {
color: var(--cta-accent);
background: transparent;
border: 1px solid color-mix(in srgb, var(--cta-accent) 42%, var(--wire-color-border));
}
.wire-cta-section__action--secondary:hover {
background: var(--cta-accent-soft);
border-color: var(--cta-accent);
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__action--primary,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__action--primary {
color: var(--cta-accent);
background: var(--cta-contrast);
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__action--secondary,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__action--secondary {
color: currentColor;
border-color: color-mix(in srgb, white 46%, transparent);
}
.wire-cta-section[data-variant="solid"] .wire-cta-section__action--secondary:hover,
.wire-cta-section[data-variant="gradient"] .wire-cta-section__action--secondary:hover {
background: color-mix(in srgb, white 12%, transparent);
border-color: color-mix(in srgb, white 68%, transparent);
}
.wire-cta-section__action-icon {
width: 1rem;
height: 1rem;
}
.wire-cta-section__action:focus-visible {
outline: 2px solid var(--wire-color-focus);
outline-offset: 3px;
}
.wire-cta-section__visual-column {
position: relative;
min-height: 0;
}
.wire-cta-section__visual {
overflow: hidden;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 1.2rem;
box-shadow: 0 20px 54px color-mix(in srgb, black 18%, transparent);
}
.wire-cta-section__visual-media {
aspect-ratio: 16 / 9;
overflow: hidden;
background: var(--wire-color-surface-soft);
}
.wire-cta-section__visual-image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.wire-cta-section__visual-header {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 0.9rem;
padding: 1.25rem;
}
.wire-cta-section__visual-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.6rem;
height: 2.6rem;
color: var(--cta-accent);
background: var(--cta-accent-soft);
border-radius: 0.82rem;
font-size: 1.15rem;
}
.wire-cta-section__visual-copy {
min-width: 0;
}
.wire-cta-section__visual-title {
margin: 0;
color: var(--wire-color-text);
font-size: 1rem;
font-weight: 650;
}
.wire-cta-section__visual-description {
margin: 0.42rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.78rem;
line-height: 1.6;
}
.wire-cta-section__visual-list {
display: grid;
border-top: 1px solid var(--wire-color-border);
}
.wire-cta-section__visual-item {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 0.7rem;
padding: 0.95rem 1.25rem;
}
.wire-cta-section__visual-item + .wire-cta-section__visual-item {
border-top: 1px solid var(--wire-color-border);
}
.wire-cta-section__visual-item-icon {
width: 1rem;
height: 1rem;
margin-top: 0.12rem;
color: var(--cta-accent);
}
.wire-cta-section__visual-item strong {
color: var(--wire-color-text);
font-size: 0.78rem;
}
.wire-cta-section__visual-item p {
margin: 0.3rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.73rem;
line-height: 1.5;
}
.wire-cta-section__footer:empty {
display: none;
}
.wire-cta-section__footer:not(:empty) {
margin-top: 2rem;
padding-top: 1.25rem;
border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}
@media (min-width: 900px) {
.wire-cta-section[data-align="split"] .wire-cta-section__layout {
grid-template-columns: minmax(0, 1.25fr) minmax(18rem, 0.75fr);
gap: 4rem;
}
.wire-cta-section[data-align="split"][data-visual-position="left"] .wire-cta-section__content {
order: 2;
}
.wire-cta-section[data-align="split"][data-visual-position="left"] .wire-cta-section__visual-column {
order: 1;
}
.wire-cta-section[data-align="split"] .wire-cta-section__visual-column:empty {
display: none;
}
.wire-cta-section[data-align="split"] .wire-cta-section__visual-column:empty + * {
grid-column: 1 / -1;
}
}
@media (max-width: 639px) {
.wire-cta-section__surface {
padding: 1.6rem;
border-radius: 1.2rem;
}
.wire-cta-section__actions {
align-items: stretch;
}
.wire-cta-section__action {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-cta-section__action {
transition: none;
}
.wire-cta-section__action:hover {
transform: none;
}
}
}
}
+504 -8
View File
@@ -3,20 +3,516 @@ component ContextMenu {
@event open = function
@event close = function
@event select = function
@event action = function
items = []
open = false
defaultOpen = false
trigger = "contextmenu"
placement = "pointer"
align = "start"
size = "default"
color = "primary"
title = "Context Menu"
variant = "raised"
title = ""
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
label = "Context menu"
closeOnSelect = true
closeOnOutside = true
disabled = false
minWidth = "14rem"
maxWidth = "20rem"
class = ""
}
state visible = defaultOpen
state positionX = 16
state positionY = 16
functions {
function isOpen() {
return open || visible
}
function showMenu(sourceEvent) {
if (disabled) {
return
}
if (sourceEvent && sourceEvent.type === "contextmenu") {
sourceEvent.preventDefault()
}
if (placement === "pointer" && sourceEvent) {
positionX = Math.max(12, Math.min(sourceEvent.clientX || 12, window.innerWidth - 340))
positionY = Math.max(12, Math.min(sourceEvent.clientY || 12, window.innerHeight - 420))
}
visible = true
$emit("open", {
x: positionX,
y: positionY,
trigger: trigger,
sourceEvent: sourceEvent
})
}
function hideMenu(reason, sourceEvent) {
visible = false
$emit("close", {
reason: reason,
sourceEvent: sourceEvent
})
}
function toggleMenu(sourceEvent) {
if (isOpen()) {
hideMenu("toggle", sourceEvent)
} else {
showMenu(sourceEvent)
}
}
function chooseItem(item, itemIndex, sourceEvent) {
if (disabled || item.disabled || item.type === "header" || item.type === "divider") {
sourceEvent.preventDefault()
return
}
$emit("select", {
item: item,
itemIndex: itemIndex,
value: item.value || "",
sourceEvent: sourceEvent
})
if (item.action) {
$emit("action", {
item: item,
itemIndex: itemIndex,
value: item.value || "",
sourceEvent: sourceEvent
})
}
if (closeOnSelect) {
hideMenu("select", sourceEvent)
}
}
function moveFocus(sourceEvent, direction) {
const root = sourceEvent.currentTarget.closest(".wire-context-menu") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-context-menu-item]:not([disabled])")]
if (!options.length) {
return
}
const activeIndex = options.indexOf(document.activeElement)
const nextIndex = (activeIndex + direction + options.length) % options.length
options[nextIndex].focus()
}
function handleKeydown(sourceEvent) {
if (sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
hideMenu("escape", sourceEvent)
} else if (sourceEvent.key === "ArrowDown") {
sourceEvent.preventDefault()
moveFocus(sourceEvent, 1)
} else if (sourceEvent.key === "ArrowUp") {
sourceEvent.preventDefault()
moveFocus(sourceEvent, -1)
}
}
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--context-menu wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
<div
{...attrs}
data-ui-component="ContextMenu"
data-open='{open || visible ? "true" : "false"}'
data-trigger='{trigger}'
data-placement='{placement}'
data-align='{align}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-context-menu {class}'
style='--wire-context-x:{positionX}px;--wire-context-y:{positionY}px;--wire-context-min-width:{minWidth};--wire-context-max-width:{maxWidth};'
@keydown='handleKeydown(event)'
>
<div
class="wire-context-menu__trigger"
tabindex='{disabled ? "-1" : "0"}'
aria-haspopup="menu"
aria-expanded='{open || visible ? "true" : "false"}'
@contextmenu='if (trigger === "contextmenu" || trigger === "both") { showMenu(event) }'
@click='if (trigger === "click" || trigger === "both") { toggleMenu(event) }'
@keydown='if (event.key === "Enter" || event.key === " ") { event.preventDefault(); toggleMenu(event) }'
>
<slot name="trigger"></slot>
</div>
{#if closeOnOutside}
<button
type="button"
class="wire-context-menu__dismiss-layer"
data-show='{open || visible}'
aria-label="Close context menu"
@click='hideMenu("outside", event)'
></button>
{/if}
<div
class="wire-context-menu__panel"
data-show='{open || visible}'
role="menu"
aria-label='{label}'
>
{#if title || description}
<div class="wire-context-menu__header">
{#if title}
<strong>{title}</strong>
{/if}
{#if description}
<span>{description}</span>
{/if}
</div>
{/if}
<slot name="header"></slot>
<div class="wire-context-menu__items">
{#each items as item, itemIndex}
{#if item.type === "divider"}
<div class="wire-context-menu__divider" role="separator"></div>
{:else if item.type === "header"}
<div class="wire-context-menu__section-label">{item.label || item.title}</div>
{:else if item.href}
<a
href='{item.href}'
target='{item.target || ""}'
rel='{item.external || item.target === "_blank" ? "noopener noreferrer" : (item.rel || "")}'
role="menuitem"
data-context-menu-item
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}'
class="wire-context-menu__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-context-menu__icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-context-menu__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-context-menu__status" aria-hidden="true"></span>
{:else if item.external}
<span class="icon-[lucide--arrow-up-right] wire-context-menu__status" aria-hidden="true"></span>
{/if}
</a>
{:else}
<button
type="button"
role="menuitem"
data-context-menu-item
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
disabled='{item.disabled}'
class="wire-context-menu__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-context-menu__icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-context-menu__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-context-menu__status" aria-hidden="true"></span>
{/if}
</button>
{/if}
{/each}
</div>
<slot></slot>
<slot name="footer"></slot>
</div>
</div>
}
style {
.wire-context-menu {
--context-accent: var(--wire-color-primary);
--context-soft: var(--wire-color-primary-soft);
position: relative;
display: block;
min-width: 0;
}
.wire-context-menu[data-color="secondary"] {
--context-accent: var(--wire-color-secondary);
--context-soft: var(--wire-color-secondary-soft);
}
.wire-context-menu[data-color="info"] {
--context-accent: var(--wire-color-info);
--context-soft: var(--wire-color-info-soft);
}
.wire-context-menu[data-color="success"] {
--context-accent: var(--wire-color-success);
--context-soft: var(--wire-color-success-soft);
}
.wire-context-menu[data-color="warning"] {
--context-accent: var(--wire-color-warning);
--context-soft: var(--wire-color-warning-soft);
}
.wire-context-menu[data-color="danger"] {
--context-accent: var(--wire-color-danger);
--context-soft: var(--wire-color-danger-soft);
}
.wire-context-menu__trigger {
display: block;
min-width: 0;
outline: none;
}
.wire-context-menu__trigger:focus-visible {
border-radius: 0.6rem;
outline: 2px solid var(--wire-color-focus);
outline-offset: 3px;
}
.wire-context-menu__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1090;
appearance: none;
padding: 0;
background: transparent;
border: 0;
}
.wire-context-menu__panel {
position: absolute;
z-index: 1091;
top: calc(100% + 0.5rem);
left: 0;
width: max-content;
min-width: var(--wire-context-min-width);
max-width: min(var(--wire-context-max-width), calc(100vw - 1.5rem));
padding: 0.45rem;
color: var(--wire-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--context-accent) 5%, transparent), transparent 58%),
color-mix(in srgb, var(--wire-color-surface-raised) 96%, transparent);
border: 1px solid color-mix(in srgb, var(--context-accent) 18%, var(--wire-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 64px color-mix(in srgb, black 24%, transparent);
backdrop-filter: blur(18px);
transform-origin: top left;
}
.wire-context-menu[data-placement="pointer"] .wire-context-menu__panel {
position: fixed;
top: var(--wire-context-y);
left: var(--wire-context-x);
}
.wire-context-menu[data-placement="bottom-end"] .wire-context-menu__panel {
right: 0;
left: auto;
transform-origin: top right;
}
.wire-context-menu[data-placement="top-start"] .wire-context-menu__panel {
top: auto;
bottom: calc(100% + 0.5rem);
transform-origin: bottom left;
}
.wire-context-menu[data-placement="top-end"] .wire-context-menu__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.5rem);
left: auto;
transform-origin: bottom right;
}
.wire-context-menu[data-variant="soft"] .wire-context-menu__panel {
background:
linear-gradient(145deg, var(--context-soft), transparent 72%),
var(--wire-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-context-menu[data-variant="outline"] .wire-context-menu__panel {
background: var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-context-menu__header {
display: grid;
gap: 0.2rem;
padding: 0.65rem 0.75rem 0.75rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-context-menu__header strong {
font-size: 0.86rem;
font-weight: 650;
}
.wire-context-menu__header span {
color: var(--wire-color-text-muted);
font-size: 0.75rem;
line-height: 1.45;
}
.wire-context-menu__items {
display: grid;
gap: 0.15rem;
padding-block: 0.25rem;
}
.wire-context-menu__section-label {
padding: 0.55rem 0.75rem 0.3rem;
color: var(--wire-color-text-muted);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.wire-context-menu__divider {
height: 1px;
margin: 0.3rem 0.45rem;
background: var(--wire-color-border);
}
.wire-context-menu__item {
appearance: none;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 0.7rem;
width: 100%;
min-width: 0;
padding: 0.65rem 0.7rem;
color: var(--wire-color-text);
background: transparent;
border: 0;
border-radius: 0.72rem;
font: inherit;
text-align: left;
text-decoration: none;
cursor: pointer;
transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.wire-context-menu__item:hover,
.wire-context-menu__item:focus-visible,
.wire-context-menu__item[data-selected="true"] {
color: var(--context-accent);
background: var(--context-soft);
outline: none;
}
.wire-context-menu__item:active {
transform: scale(0.985);
}
.wire-context-menu__item[data-danger="true"] {
color: var(--wire-color-danger);
}
.wire-context-menu__item[data-danger="true"]:hover,
.wire-context-menu__item[data-danger="true"]:focus-visible {
background: var(--wire-color-danger-soft);
}
.wire-context-menu__item[disabled],
.wire-context-menu__item[aria-disabled="true"] {
opacity: 0.48;
pointer-events: none;
}
.wire-context-menu__icon,
.wire-context-menu__status {
width: 1rem;
height: 1rem;
color: currentColor;
}
.wire-context-menu__copy {
display: grid;
gap: 0.12rem;
min-width: 0;
}
.wire-context-menu__copy strong {
overflow: hidden;
font-size: 0.82rem;
font-weight: 600;
line-height: 1.3;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-context-menu__copy small {
overflow: hidden;
color: var(--wire-color-text-muted);
font-size: 0.7rem;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-context-menu kbd {
padding: 0.16rem 0.38rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: 0.36rem;
font-size: 0.64rem;
font-family: inherit;
}
.wire-context-menu[data-size="sm"] .wire-context-menu__item {
padding: 0.52rem 0.6rem;
}
.wire-context-menu[data-size="lg"] .wire-context-menu__item {
padding: 0.78rem 0.82rem;
}
@media (max-width: 639px) {
.wire-context-menu[data-placement="pointer"] .wire-context-menu__panel {
right: 0.75rem;
bottom: 0.75rem;
left: 0.75rem;
top: auto;
width: auto;
max-width: none;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-context-menu__item {
transition: none;
}
}
}
}
+438 -8
View File
@@ -2,20 +2,450 @@ component Drawer {
props {
@event open = function
@event close = function
size = "default"
@event cancel = function
open = false
defaultOpen = false
placement = "right"
size = "md"
color = "primary"
variant = "default"
title = "Drawer"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
icon = ""
label = "Drawer"
closeLabel = "Close drawer"
showClose = true
closeOnBackdrop = true
closeOnEscape = true
overlay = true
scrollable = true
triggerLabel = ""
triggerIcon = ""
class = ""
}
state visible = defaultOpen
functions {
function isOpen() {
return open || visible
}
function showDrawer(sourceEvent) {
visible = true
$emit("open", {
placement: placement,
sourceEvent: sourceEvent
})
}
function hideDrawer(reason, sourceEvent) {
visible = false
$emit("close", {
reason: reason,
placement: placement,
sourceEvent: sourceEvent
})
}
function cancelDrawer(sourceEvent) {
$emit("cancel", {
placement: placement,
sourceEvent: sourceEvent
})
hideDrawer("cancel", sourceEvent)
}
function handleKeydown(sourceEvent) {
if (closeOnEscape && sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
cancelDrawer(sourceEvent)
}
}
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--drawer wire-next--placement-{placement} {class}" role="dialog" aria-modal="true" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
<div
{...attrs}
data-ui-component="Drawer"
data-open='{open || visible ? "true" : "false"}'
data-placement='{placement}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-overlay='{overlay ? "true" : "false"}'
data-scrollable='{scrollable ? "true" : "false"}'
class='wire-drawer {class}'
>
{#if triggerLabel}
<button
type="button"
class="wire-drawer__trigger"
aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}'
@click='showDrawer(event)'
>
{#if triggerIcon}
<span class='{triggerIcon}' aria-hidden="true"></span>
{/if}
<span>{triggerLabel}</span>
</button>
{/if}
<span class="wire-drawer__trigger-slot" @click='showDrawer(event)'>
<slot name="trigger"></slot>
</span>
<div
class="wire-drawer__layer"
data-show='{open || visible}'
role="presentation"
@keydown='handleKeydown(event)'
>
{#if overlay}
<button
type="button"
class="wire-drawer__backdrop"
aria-label='{closeLabel}'
@click='if (closeOnBackdrop) { cancelDrawer(event) }'
></button>
{/if}
<section
class="wire-drawer__panel"
role="dialog"
aria-modal="true"
aria-label='{label || title}'
tabindex="-1"
>
<div class="wire-drawer__handle" aria-hidden="true"></div>
<header class="wire-drawer__header">
<div class="wire-drawer__heading">
{#if icon}
<span class='wire-drawer__icon {icon}' aria-hidden="true"></span>
{/if}
<div class="wire-drawer__heading-copy">
<slot name="header"></slot>
{#if title}
<h2>{title}</h2>
{/if}
{#if description}
<p>{description}</p>
{/if}
</div>
</div>
{#if showClose}
<button
type="button"
class="wire-drawer__close"
aria-label='{closeLabel}'
@click='hideDrawer("close-button", event)'
>
<span class="icon-[lucide--x]" aria-hidden="true"></span>
</button>
{/if}
</header>
<div class="wire-drawer__body">
<slot></slot>
</div>
<footer class="wire-drawer__footer">
<slot name="footer"></slot>
</footer>
</section>
</div>
</div>
}
style {
.wire-drawer {
--drawer-accent: var(--wire-color-primary);
--drawer-soft: var(--wire-color-primary-soft);
position: relative;
display: inline-flex;
}
.wire-drawer[data-color="secondary"] {
--drawer-accent: var(--wire-color-secondary);
--drawer-soft: var(--wire-color-secondary-soft);
}
.wire-drawer[data-color="info"] {
--drawer-accent: var(--wire-color-info);
--drawer-soft: var(--wire-color-info-soft);
}
.wire-drawer[data-color="success"] {
--drawer-accent: var(--wire-color-success);
--drawer-soft: var(--wire-color-success-soft);
}
.wire-drawer[data-color="warning"] {
--drawer-accent: var(--wire-color-warning);
--drawer-soft: var(--wire-color-warning-soft);
}
.wire-drawer[data-color="danger"] {
--drawer-accent: var(--wire-color-danger);
--drawer-soft: var(--wire-color-danger-soft);
}
.wire-drawer__trigger,
.wire-drawer__trigger-slot {
display: inline-flex;
align-items: center;
gap: 0.55rem;
}
.wire-drawer__trigger {
appearance: none;
min-height: 2.55rem;
padding: 0.65rem 1rem;
color: var(--wire-color-primary-contrast);
background: var(--drawer-accent);
border: 0;
border-radius: 0.8rem;
font: inherit;
font-size: 0.85rem;
font-weight: 650;
cursor: pointer;
}
.wire-drawer__layer {
position: fixed;
inset: 0;
z-index: 1200;
display: flex;
pointer-events: none;
}
.wire-drawer__backdrop {
position: absolute;
inset: 0;
z-index: 0;
appearance: none;
padding: 0;
background: color-mix(in srgb, black 56%, transparent);
border: 0;
backdrop-filter: blur(7px);
pointer-events: auto;
}
.wire-drawer[data-overlay="false"] .wire-drawer__backdrop {
display: none;
}
.wire-drawer__panel {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
width: min(28rem, calc(100vw - 1rem));
max-height: 100%;
margin-left: auto;
color: var(--wire-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--drawer-accent) 8%, transparent),
transparent 34%
),
var(--wire-color-surface-raised);
border-left: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
box-shadow: -28px 0 80px color-mix(in srgb, black 28%, transparent);
pointer-events: auto;
overflow: hidden;
}
.wire-drawer[data-size="sm"] .wire-drawer__panel {
width: min(22rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="lg"] .wire-drawer__panel {
width: min(38rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="xl"] .wire-drawer__panel {
width: min(52rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="full"] .wire-drawer__panel {
width: 100vw;
}
.wire-drawer[data-placement="left"] .wire-drawer__panel {
margin-right: auto;
margin-left: 0;
border-right: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
border-left: 0;
box-shadow: 28px 0 80px color-mix(in srgb, black 28%, transparent);
}
.wire-drawer[data-placement="top"] .wire-drawer__layer,
.wire-drawer[data-placement="bottom"] .wire-drawer__layer {
align-items: flex-start;
}
.wire-drawer[data-placement="top"] .wire-drawer__panel,
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
width: 100%;
max-height: min(80vh, 44rem);
margin: 0;
border: 0;
box-shadow: 0 24px 80px color-mix(in srgb, black 28%, transparent);
}
.wire-drawer[data-placement="top"] .wire-drawer__panel {
border-bottom: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
}
.wire-drawer[data-placement="bottom"] .wire-drawer__layer {
align-items: flex-end;
}
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
border-top: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
border-radius: 1.3rem 1.3rem 0 0;
}
.wire-drawer[data-variant="soft"] .wire-drawer__panel {
background:
linear-gradient(145deg, var(--drawer-soft), transparent 65%),
var(--wire-color-surface-raised);
}
.wire-drawer[data-variant="solid"] .wire-drawer__panel {
color: var(--wire-color-primary-contrast);
background: var(--drawer-accent);
border-color: color-mix(in srgb, white 18%, transparent);
}
.wire-drawer__handle {
display: none;
width: 2.75rem;
height: 0.28rem;
margin: 0.55rem auto 0;
background: color-mix(in srgb, var(--wire-color-text-muted) 48%, transparent);
border-radius: 999px;
}
.wire-drawer[data-placement="bottom"] .wire-drawer__handle {
display: block;
}
.wire-drawer__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
padding: 1.35rem 1.35rem 1.1rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-drawer__heading {
display: flex;
align-items: flex-start;
gap: 0.85rem;
min-width: 0;
}
.wire-drawer__icon {
flex: 0 0 auto;
width: 1.25rem;
height: 1.25rem;
margin-top: 0.15rem;
color: var(--drawer-accent);
}
.wire-drawer[data-variant="solid"] .wire-drawer__icon {
color: currentColor;
}
.wire-drawer__heading-copy {
display: grid;
gap: 0.3rem;
min-width: 0;
}
.wire-drawer__heading-copy h2,
.wire-drawer__heading-copy p {
margin: 0;
}
.wire-drawer__heading-copy h2 {
font-size: 1.05rem;
font-weight: 650;
line-height: 1.3;
}
.wire-drawer__heading-copy p {
color: var(--wire-color-text-muted);
font-size: 0.8rem;
line-height: 1.55;
}
.wire-drawer[data-variant="solid"] .wire-drawer__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
.wire-drawer__close {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.35rem;
height: 2.35rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: 0.75rem;
cursor: pointer;
}
.wire-drawer__close:hover,
.wire-drawer__close:focus-visible {
color: var(--drawer-accent);
border-color: color-mix(in srgb, var(--drawer-accent) 34%, var(--wire-color-border));
outline: none;
}
.wire-drawer__body {
flex: 1 1 auto;
min-height: 0;
padding: 1.35rem;
}
.wire-drawer[data-scrollable="true"] .wire-drawer__body {
overflow: auto;
overscroll-behavior: contain;
}
.wire-drawer__footer {
padding: 1rem 1.35rem 1.35rem;
border-top: 1px solid var(--wire-color-border);
}
.wire-drawer__footer:empty {
display: none;
}
@media (max-width: 639px) {
.wire-drawer[data-placement="right"] .wire-drawer__panel,
.wire-drawer[data-placement="left"] .wire-drawer__panel {
width: min(24rem, 100vw);
}
.wire-drawer__header,
.wire-drawer__body,
.wire-drawer__footer {
padding-inline: 1rem;
}
}
}
}
+547 -30
View File
@@ -4,50 +4,567 @@ component Dropdown {
@event open = function
@event close = function
@event select = function
@event action = function
items = []
open = false
defaultOpen = false
label = "Open menu"
icon = ""
showChevron = true
menuLabel = "Dropdown menu"
placement = "bottom-start"
width = "md"
size = "default"
color = "primary"
label = "Open menu"
items = []
placement = "end"
variant = "raised"
closeOnSelect = true
closeOnOutside = true
disabled = false
emptyLabel = "No menu items"
class = ""
}
state visible = defaultOpen
functions {
function toggleMenu(event) {
$emit("toggle", { open: event.currentTarget.open })
$emit(event.currentTarget.open ? "open" : "close", { source: "trigger" })
function isOpen() {
return open || visible
}
function selectItem(item) {
$emit("select", { item: item, value: item.value || "" })
function showMenu(sourceEvent) {
if (disabled) {
return
}
visible = true
$emit("open", { sourceEvent: sourceEvent })
$emit("toggle", { open: true, sourceEvent: sourceEvent })
}
function hideMenu(reason, sourceEvent) {
visible = false
$emit("close", { reason: reason, sourceEvent: sourceEvent })
$emit("toggle", { open: false, reason: reason, sourceEvent: sourceEvent })
}
function toggleMenu(sourceEvent) {
if (isOpen()) {
hideMenu("toggle", sourceEvent)
} else {
showMenu(sourceEvent)
}
}
function chooseItem(item, itemIndex, sourceEvent) {
if (disabled || item.disabled || item.type === "divider" || item.type === "header") {
sourceEvent.preventDefault()
return
}
$emit("select", {
item: item,
itemIndex: itemIndex,
value: item.value || "",
sourceEvent: sourceEvent
})
if (item.action) {
$emit("action", {
item: item,
itemIndex: itemIndex,
value: item.value || "",
sourceEvent: sourceEvent
})
}
if (closeOnSelect) {
hideMenu("select", sourceEvent)
}
}
function moveFocus(sourceEvent, direction) {
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-dropdown-item]:not([disabled])")]
if (!options.length) {
return
}
const activeIndex = options.indexOf(document.activeElement)
const nextIndex = (activeIndex + direction + options.length) % options.length
options[nextIndex].focus()
}
function handleKeydown(sourceEvent) {
if (sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
hideMenu("escape", sourceEvent)
} else if (sourceEvent.key === "ArrowDown") {
sourceEvent.preventDefault()
if (!isOpen()) {
showMenu(sourceEvent)
}
moveFocus(sourceEvent, 1)
} else if (sourceEvent.key === "ArrowUp") {
sourceEvent.preventDefault()
if (!isOpen()) {
showMenu(sourceEvent)
}
moveFocus(sourceEvent, -1)
} else if (sourceEvent.key === "Home" && isOpen()) {
sourceEvent.preventDefault()
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const first = root.querySelector("[data-dropdown-item]:not([disabled])")
if (first) {
first.focus()
}
} else if (sourceEvent.key === "End" && isOpen()) {
sourceEvent.preventDefault()
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-dropdown-item]:not([disabled])")]
if (options.length) {
options[options.length - 1].focus()
}
}
}
}
view {
<details class="wire-dropdown wire-dropdown--{placement} wire-next--color-{color} wire-next--size-{size} {class}" data-wrn-dropdown @toggle="toggleMenu($event)">
<summary aria-label="{label}">
<slot name="trigger" />
<span class="wire-dropdown__chevron icon-[lucide--chevron-down]" aria-hidden="true"></span>
</summary>
<div class="wire-dropdown__panel" role="menu" aria-label="{label}">
<slot name="header" />
{#each items as item}
{#if item.type === "divider"}
<hr />
{:else}
{#if item.type === "header"}
<span class="wire-dropdown__heading">{item.label}</span>
{:else}
<a class="wire-dropdown__item {item.danger ? 'wire-dropdown__item--danger' : ''}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" role="menuitem" @click="selectItem(item)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span><strong>{item.label}</strong>{#if item.description}<small>{item.description}</small>{/if}</span>
{#if item.external}<span class="icon-[lucide--arrow-up-right]" aria-hidden="true"></span>{/if}
<div
{...attrs}
data-ui-component="Dropdown"
data-open='{open || visible ? "true" : "false"}'
data-placement='{placement}'
data-width='{width}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-dropdown {class}'
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-dropdown__trigger"
disabled='{disabled}'
aria-haspopup="menu"
aria-expanded='{open || visible ? "true" : "false"}'
@click='toggleMenu(event)'
>
<slot name="trigger"></slot>
{#if icon}
<span class='wire-dropdown__trigger-icon {icon}' aria-hidden="true"></span>
{/if}
{#if label}
<span class="wire-dropdown__trigger-label">{label}</span>
{/if}
{#if showChevron}
<span class="icon-[lucide--chevron-down] wire-dropdown__chevron" aria-hidden="true"></span>
{/if}
</button>
{#if closeOnOutside}
<button
type="button"
class="wire-dropdown__dismiss-layer"
data-show='{open || visible}'
aria-label="Close menu"
@click='hideMenu("outside", event)'
></button>
{/if}
<div
class="wire-dropdown__panel"
data-show='{open || visible}'
role="menu"
aria-label='{menuLabel}'
>
<slot name="header"></slot>
<div class="wire-dropdown__items">
{#each items as item, itemIndex}
{#if item.type === "divider"}
<div class="wire-dropdown__divider" role="separator"></div>
{:else if item.type === "header"}
<div class="wire-dropdown__section-label">{item.label || item.title}</div>
{:else if item.href}
<a
href='{item.href}'
target='{item.target || ""}'
rel='{item.external || item.target === "_blank" ? "noopener noreferrer" : (item.rel || "")}'
role="menuitem"
data-dropdown-item
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}'
class="wire-dropdown__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-dropdown__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.badge}
<span class="wire-dropdown__badge">{item.badge}</span>
{:else if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-dropdown__status" aria-hidden="true"></span>
{:else if item.external}
<span class="icon-[lucide--arrow-up-right] wire-dropdown__status" aria-hidden="true"></span>
{/if}
</a>
{:else}
<button
type="button"
role="menuitem"
data-dropdown-item
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
disabled='{item.disabled}'
class="wire-dropdown__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-dropdown__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.badge}
<span class="wire-dropdown__badge">{item.badge}</span>
{:else if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-dropdown__status" aria-hidden="true"></span>
{/if}
</button>
{/if}
{/if}
{/each}
<slot name="footer" />
{:empty}
<div class="wire-dropdown__empty">{emptyLabel}</div>
{/each}
</div>
<slot></slot>
<slot name="footer"></slot>
</div>
</details>
</div>
}
style {
.wire-dropdown {
--dropdown-accent: var(--wire-color-primary);
--dropdown-soft: var(--wire-color-primary-soft);
position: relative;
display: inline-flex;
min-width: 0;
}
.wire-dropdown[data-color="secondary"] {
--dropdown-accent: var(--wire-color-secondary);
--dropdown-soft: var(--wire-color-secondary-soft);
}
.wire-dropdown[data-color="info"] {
--dropdown-accent: var(--wire-color-info);
--dropdown-soft: var(--wire-color-info-soft);
}
.wire-dropdown[data-color="success"] {
--dropdown-accent: var(--wire-color-success);
--dropdown-soft: var(--wire-color-success-soft);
}
.wire-dropdown[data-color="warning"] {
--dropdown-accent: var(--wire-color-warning);
--dropdown-soft: var(--wire-color-warning-soft);
}
.wire-dropdown[data-color="danger"] {
--dropdown-accent: var(--wire-color-danger);
--dropdown-soft: var(--wire-color-danger-soft);
}
.wire-dropdown__trigger {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-height: 2.55rem;
padding: 0.65rem 0.9rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 0.78rem;
font: inherit;
font-size: 0.84rem;
font-weight: 600;
cursor: pointer;
transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
}
.wire-dropdown__trigger:hover,
.wire-dropdown__trigger:focus-visible,
.wire-dropdown[data-open="true"] .wire-dropdown__trigger {
color: var(--dropdown-accent);
background: var(--dropdown-soft);
border-color: color-mix(in srgb, var(--dropdown-accent) 38%, var(--wire-color-border));
outline: none;
}
.wire-dropdown__trigger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.wire-dropdown__trigger-icon,
.wire-dropdown__chevron {
width: 1rem;
height: 1rem;
}
.wire-dropdown__chevron {
transition: transform 160ms ease;
}
.wire-dropdown[data-open="true"] .wire-dropdown__chevron {
transform: rotate(180deg);
}
.wire-dropdown[data-size="sm"] .wire-dropdown__trigger {
min-height: 2.2rem;
padding: 0.5rem 0.72rem;
font-size: 0.78rem;
}
.wire-dropdown[data-size="lg"] .wire-dropdown__trigger {
min-height: 2.9rem;
padding: 0.78rem 1.05rem;
font-size: 0.9rem;
}
.wire-dropdown__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1100;
appearance: none;
padding: 0;
background: transparent;
border: 0;
}
.wire-dropdown__panel {
position: absolute;
z-index: 1101;
top: calc(100% + 0.55rem);
left: 0;
width: max-content;
min-width: 12rem;
max-width: min(22rem, calc(100vw - 1.5rem));
padding: 0.45rem;
color: var(--wire-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--dropdown-accent) 5%, transparent), transparent 60%),
color-mix(in srgb, var(--wire-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--dropdown-accent) 18%, var(--wire-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 64px color-mix(in srgb, black 22%, transparent);
backdrop-filter: blur(18px);
}
.wire-dropdown[data-width="trigger"] .wire-dropdown__panel {
width: 100%;
min-width: 100%;
}
.wire-dropdown[data-width="sm"] .wire-dropdown__panel {
width: 12rem;
}
.wire-dropdown[data-width="md"] .wire-dropdown__panel {
width: 17rem;
}
.wire-dropdown[data-width="lg"] .wire-dropdown__panel {
width: 22rem;
}
.wire-dropdown[data-placement="bottom-end"] .wire-dropdown__panel {
right: 0;
left: auto;
}
.wire-dropdown[data-placement="top-start"] .wire-dropdown__panel {
top: auto;
bottom: calc(100% + 0.55rem);
}
.wire-dropdown[data-placement="top-end"] .wire-dropdown__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.55rem);
left: auto;
}
.wire-dropdown[data-variant="soft"] .wire-dropdown__panel {
background:
linear-gradient(145deg, var(--dropdown-soft), transparent 70%),
var(--wire-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-dropdown[data-variant="outline"] .wire-dropdown__panel {
background: var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-dropdown__items {
display: grid;
gap: 0.15rem;
}
.wire-dropdown__section-label {
padding: 0.55rem 0.72rem 0.3rem;
color: var(--wire-color-text-muted);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.wire-dropdown__divider {
height: 1px;
margin: 0.3rem 0.4rem;
background: var(--wire-color-border);
}
.wire-dropdown__item {
appearance: none;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 0.7rem;
width: 100%;
min-width: 0;
padding: 0.68rem 0.72rem;
color: var(--wire-color-text);
background: transparent;
border: 0;
border-radius: 0.72rem;
font: inherit;
text-align: left;
text-decoration: none;
cursor: pointer;
transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.wire-dropdown__item:hover,
.wire-dropdown__item:focus-visible,
.wire-dropdown__item[data-selected="true"] {
color: var(--dropdown-accent);
background: var(--dropdown-soft);
outline: none;
}
.wire-dropdown__item:active {
transform: scale(0.985);
}
.wire-dropdown__item[data-danger="true"] {
color: var(--wire-color-danger);
}
.wire-dropdown__item[data-danger="true"]:hover,
.wire-dropdown__item[data-danger="true"]:focus-visible {
background: var(--wire-color-danger-soft);
}
.wire-dropdown__item[disabled],
.wire-dropdown__item[aria-disabled="true"] {
opacity: 0.48;
pointer-events: none;
}
.wire-dropdown__item-icon,
.wire-dropdown__status {
width: 1rem;
height: 1rem;
}
.wire-dropdown__copy {
display: grid;
gap: 0.12rem;
min-width: 0;
}
.wire-dropdown__copy strong {
overflow: hidden;
font-size: 0.82rem;
font-weight: 600;
line-height: 1.3;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-dropdown__copy small {
overflow: hidden;
color: var(--wire-color-text-muted);
font-size: 0.7rem;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-dropdown__badge {
padding: 0.18rem 0.45rem;
color: var(--dropdown-accent);
background: var(--dropdown-soft);
border-radius: 999px;
font-size: 0.64rem;
font-weight: 700;
}
.wire-dropdown kbd {
padding: 0.16rem 0.38rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: 0.36rem;
font-size: 0.64rem;
font-family: inherit;
}
.wire-dropdown__empty {
padding: 1rem;
color: var(--wire-color-text-muted);
font-size: 0.8rem;
text-align: center;
}
@media (max-width: 639px) {
.wire-dropdown__panel {
position: fixed;
right: 0.75rem;
bottom: 0.75rem;
left: 0.75rem;
top: auto;
width: auto;
min-width: 0;
max-width: none;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-dropdown__trigger,
.wire-dropdown__chevron,
.wire-dropdown__item {
transition: none;
}
}
}
}
+699 -83
View File
@@ -1,11 +1,27 @@
component FeatureCard {
props {
icon = ""
iconStyle = "soft"
iconSize = "default"
eyebrow = ""
title = "Feature"
description = ""
image = ""
imageAlt = ""
imagePosition = "top"
imageAspect = "wide"
imageLoading = "lazy"
href = ""
target = ""
rel = ""
external = false
actionLabel = "Learn more"
actionIcon = ""
showArrow = true
stretchedLink = true
badge = ""
badgeColor = "primary"
size = "default"
@@ -19,95 +35,695 @@ component FeatureCard {
view {
<article
{...attrs}
data-ui-component="FeatureCard"
aria-disabled='{disabled}'
class='group relative flex h-full min-w-0 flex-col overflow-hidden rounded-2xl border border-[var(--wire-color-border)] bg-[var(--wire-color-surface-raised)] shadow-sm transition duration-200 {class}'
class:p-4='size === "sm"'
class:p-6='size === "default" || size === "md"'
class:p-8='size === "lg"'
class:bg-[var(--wire-color-primary-soft)]='variant === "soft" && color === "primary"'
class:bg-[var(--wire-color-success-soft)]='variant === "soft" && color === "success"'
class:bg-[var(--wire-color-warning-soft)]='variant === "soft" && color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='variant === "soft" && color === "danger"'
class:bg-transparent='variant === "ghost"'
class:border-transparent='variant === "ghost"'
class:shadow-none='variant === "ghost" || variant === "outline"'
class:hover:-translate-y-1='hover === "lift" && !disabled'
class:hover:shadow-xl='(hover === "lift" || hover === "shadow") && !disabled'
class:hover:border-[var(--wire-color-primary)]='(hover === "lift" || hover === "border") && !disabled'
class:text-center='align === "center"'
class:items-center='align === "center"'
class:text-right='align === "right"'
class:items-end='align === "right"'
class:opacity-60='disabled'
class:pointer-events-none='disabled'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-hover='{hover}'
data-align='{align}'
data-icon-style='{iconStyle}'
data-icon-size='{iconSize}'
data-image-position='{imagePosition}'
data-image-aspect='{imageAspect}'
data-disabled='{disabled ? "true" : "false"}'
data-stretched-link='{stretchedLink ? "true" : "false"}'
class='wire-feature-card {class}'
role="listitem"
aria-disabled='{disabled ? "true" : "false"}'
>
<div class="flex w-full items-start justify-between gap-4">
<div
class="flex size-12 shrink-0 items-center justify-center rounded-2xl bg-[var(--wire-color-primary-soft)] text-[var(--wire-color-primary)]"
class:bg-[var(--wire-color-success-soft)]='color === "success"'
class:text-[var(--wire-color-success)]='color === "success"'
class:bg-[var(--wire-color-warning-soft)]='color === "warning"'
class:text-[var(--wire-color-warning-text)]='color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='color === "danger"'
class:text-[var(--wire-color-danger)]='color === "danger"'
class:bg-[var(--wire-color-info-soft)]='color === "info"'
class:text-[var(--wire-color-info)]='color === "info"'
class:size-10='size === "sm"'
class:size-14='size === "lg"'
>
<slot name="icon"></slot>
{#if icon}
<span class='{icon + " size-6"}' aria-hidden="true"></span>
{#if image}
<div class="wire-feature-card__media">
<img
src='{image}'
alt='{imageAlt}'
loading='{imageLoading}'
class="wire-feature-card__image"
/>
<div class="wire-feature-card__media-overlay" aria-hidden="true"></div>
</div>
{/if}
<slot name="media"></slot>
<div class="wire-feature-card__body">
<div class="wire-feature-card__top">
<div class="wire-feature-card__leading">
<slot name="icon"></slot>
{#if icon}
<div class="wire-feature-card__icon" aria-hidden="true">
<span class='{icon}'></span>
</div>
{/if}
</div>
{#if badge}
<span class="wire-feature-card__badge" data-color='{badgeColor}'>
{badge}
</span>
{/if}
</div>
{#if badge}
<Badge
label='{badge}'
size="sm"
color='{badgeColor}'
variant="soft"
/>
{/if}
<div class="wire-feature-card__copy">
{#if eyebrow}
<p class="wire-feature-card__eyebrow">{eyebrow}</p>
{/if}
<h3 class="wire-feature-card__title">{title}</h3>
{#if description}
<p class="wire-feature-card__description">{description}</p>
{/if}
</div>
<div class="wire-feature-card__content">
<slot></slot>
</div>
<div class="wire-feature-card__footer">
<div class="wire-feature-card__footer-content">
<slot name="footer"></slot>
</div>
{#if href && actionLabel && !disabled}
<a
href='{href}'
target='{target}'
rel='{external || target === "_blank" ? "noopener noreferrer" : rel}'
class="wire-feature-card__action"
aria-label='{actionLabel + ": " + title}'
>
<span>{actionLabel}</span>
{#if actionIcon}
<span class='wire-feature-card__action-icon {actionIcon}' aria-hidden="true"></span>
{:else if external}
<span class="wire-feature-card__action-icon icon-[lucide--external-link]" aria-hidden="true"></span>
{:else if showArrow}
<span class="wire-feature-card__action-icon icon-[lucide--arrow-right]" aria-hidden="true"></span>
{/if}
</a>
{:else if actionLabel}
<span class="wire-feature-card__action wire-feature-card__action--disabled">
<span>{actionLabel}</span>
</span>
{/if}
</div>
</div>
<h3 class="mt-5 text-lg font-bold leading-tight text-[var(--wire-color-text)]" class:text-xl='size === "lg"'>
{title}
</h3>
{#if description}
<p class="mt-3 flex-1 text-sm leading-6 text-[var(--wire-color-text-muted)]" class:text-base='size === "lg"'>
{description}
</p>
{/if}
<slot></slot>
<div class="mt-6 flex w-full items-center justify-between gap-3">
<slot name="footer"></slot>
{#if href && actionLabel}
<TextLink
label='{actionLabel}'
href='{href}'
icon='{actionIcon}'
iconPosition="start"
showArrow="true"
size="sm"
color='{color}'
class="ml-auto"
/>
{/if}
</div>
{#if href}
<a
href='{href}'
aria-label='{title}'
class="absolute inset-0 z-10 rounded-2xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--wire-color-focus)]"
></a>
{/if}
</article>
}
style {
.wire-feature-card {
--feature-accent: var(--wire-color-primary);
--feature-accent-hover: var(--wire-color-primary-hover);
--feature-accent-soft: var(--wire-color-primary-soft);
--feature-accent-muted: var(--wire-color-primary-muted);
--feature-contrast: var(--wire-color-primary-contrast);
--feature-border: color-mix(in srgb, var(--feature-accent) 16%, var(--wire-color-border));
position: relative;
isolation: isolate;
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
height: 100%;
overflow: hidden;
color: var(--wire-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--feature-accent) 7%, transparent),
transparent 34%
),
var(--wire-color-surface-raised);
border: 1px solid var(--feature-border);
border-radius: 1.25rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 4%, transparent) inset,
0 10px 30px color-mix(in srgb, black 9%, transparent);
transition:
transform 180ms ease,
border-color 180ms ease,
box-shadow 180ms ease,
background-color 180ms ease;
}
.wire-feature-card[data-color="secondary"] {
--feature-accent: var(--wire-color-secondary);
--feature-accent-hover: var(--wire-color-secondary-hover);
--feature-accent-soft: var(--wire-color-secondary-soft);
--feature-accent-muted: var(--wire-color-secondary-muted);
--feature-contrast: var(--wire-color-secondary-contrast);
}
.wire-feature-card[data-color="info"] {
--feature-accent: var(--wire-color-info);
--feature-accent-soft: var(--wire-color-info-soft);
--feature-accent-muted: var(--wire-color-info-muted);
--feature-contrast: var(--wire-color-info-contrast);
}
.wire-feature-card[data-color="success"] {
--feature-accent: var(--wire-color-success);
--feature-accent-soft: var(--wire-color-success-soft);
--feature-accent-muted: var(--wire-color-success-muted);
--feature-contrast: var(--wire-color-success-contrast);
}
.wire-feature-card[data-color="warning"] {
--feature-accent: var(--wire-color-warning);
--feature-accent-soft: var(--wire-color-warning-soft);
--feature-accent-muted: var(--wire-color-warning-muted);
--feature-contrast: var(--wire-color-warning-contrast);
}
.wire-feature-card[data-color="danger"] {
--feature-accent: var(--wire-color-danger);
--feature-accent-soft: var(--wire-color-danger-soft);
--feature-accent-muted: var(--wire-color-danger-muted);
--feature-contrast: var(--wire-color-danger-contrast);
}
.wire-feature-card::before {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: -1;
height: 3px;
content: "";
background: linear-gradient(90deg, var(--feature-accent), transparent 72%);
opacity: 0.8;
}
.wire-feature-card__media {
position: relative;
overflow: hidden;
background: var(--wire-color-surface-soft);
border-bottom: 1px solid var(--feature-border);
}
.wire-feature-card[data-image-aspect="square"] .wire-feature-card__media {
aspect-ratio: 1;
}
.wire-feature-card[data-image-aspect="portrait"] .wire-feature-card__media {
aspect-ratio: 4 / 5;
}
.wire-feature-card[data-image-aspect="standard"] .wire-feature-card__media {
aspect-ratio: 4 / 3;
}
.wire-feature-card[data-image-aspect="wide"] .wire-feature-card__media {
aspect-ratio: 16 / 9;
}
.wire-feature-card[data-image-aspect="cinema"] .wire-feature-card__media {
aspect-ratio: 21 / 9;
}
.wire-feature-card__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 300ms ease;
}
.wire-feature-card__media-overlay {
position: absolute;
inset: 0;
pointer-events: none;
background: linear-gradient(180deg, transparent 58%, color-mix(in srgb, black 18%, transparent));
}
.wire-feature-card__body {
display: flex;
flex: 1;
flex-direction: column;
min-width: 0;
padding: 1.5rem;
}
.wire-feature-card[data-size="sm"] .wire-feature-card__body {
padding: 1.1rem;
}
.wire-feature-card[data-size="lg"] .wire-feature-card__body {
padding: 1.85rem;
}
.wire-feature-card__top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
min-width: 0;
}
.wire-feature-card__leading {
display: flex;
align-items: center;
min-width: 0;
}
.wire-feature-card__icon {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
color: var(--feature-accent);
background: var(--feature-accent-soft);
border: 1px solid color-mix(in srgb, var(--feature-accent) 18%, transparent);
border-radius: 0.95rem;
box-shadow: 0 1px 0 color-mix(in srgb, white 7%, transparent) inset;
}
.wire-feature-card__icon > span {
width: 1.35rem;
height: 1.35rem;
}
.wire-feature-card[data-icon-size="sm"] .wire-feature-card__icon {
width: 2.5rem;
height: 2.5rem;
border-radius: 0.8rem;
}
.wire-feature-card[data-icon-size="sm"] .wire-feature-card__icon > span {
width: 1.1rem;
height: 1.1rem;
}
.wire-feature-card[data-icon-size="lg"] .wire-feature-card__icon {
width: 3.6rem;
height: 3.6rem;
border-radius: 1.05rem;
}
.wire-feature-card[data-icon-size="lg"] .wire-feature-card__icon > span {
width: 1.65rem;
height: 1.65rem;
}
.wire-feature-card[data-icon-style="solid"] .wire-feature-card__icon {
color: var(--feature-contrast);
background: var(--feature-accent);
border-color: color-mix(in srgb, white 18%, transparent);
box-shadow: 0 10px 24px color-mix(in srgb, var(--feature-accent) 24%, transparent);
}
.wire-feature-card[data-icon-style="outline"] .wire-feature-card__icon {
color: var(--feature-accent);
background: transparent;
border-color: color-mix(in srgb, var(--feature-accent) 38%, var(--wire-color-border));
box-shadow: none;
}
.wire-feature-card[data-icon-style="ghost"] .wire-feature-card__icon {
width: auto;
height: auto;
padding: 0.25rem;
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-feature-card__badge {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
min-height: 1.65rem;
padding: 0.25rem 0.65rem;
color: var(--feature-accent);
font-size: 0.7rem;
font-weight: 600;
line-height: 1;
white-space: nowrap;
background: var(--feature-accent-soft);
border: 1px solid color-mix(in srgb, var(--feature-accent) 18%, transparent);
border-radius: 999px;
}
.wire-feature-card__badge[data-color="secondary"] {
color: var(--wire-color-secondary);
background: var(--wire-color-secondary-soft);
border-color: color-mix(in srgb, var(--wire-color-secondary) 20%, transparent);
}
.wire-feature-card__badge[data-color="info"] {
color: var(--wire-color-info);
background: var(--wire-color-info-soft);
border-color: color-mix(in srgb, var(--wire-color-info) 20%, transparent);
}
.wire-feature-card__badge[data-color="success"] {
color: var(--wire-color-success);
background: var(--wire-color-success-soft);
border-color: color-mix(in srgb, var(--wire-color-success) 20%, transparent);
}
.wire-feature-card__badge[data-color="warning"] {
color: var(--wire-color-warning-text);
background: var(--wire-color-warning-soft);
border-color: color-mix(in srgb, var(--wire-color-warning) 22%, transparent);
}
.wire-feature-card__badge[data-color="danger"] {
color: var(--wire-color-danger);
background: var(--wire-color-danger-soft);
border-color: color-mix(in srgb, var(--wire-color-danger) 20%, transparent);
}
.wire-feature-card__copy {
min-width: 0;
margin-top: 1.25rem;
}
.wire-feature-card__eyebrow {
margin: 0 0 0.5rem;
color: var(--feature-accent);
font-size: 0.72rem;
font-weight: 600;
line-height: 1.3;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.wire-feature-card__title {
margin: 0;
color: var(--wire-color-text);
font-size: 1.1rem;
font-weight: 600;
line-height: 1.35;
letter-spacing: -0.015em;
text-wrap: balance;
transition: color 180ms ease;
}
.wire-feature-card[data-size="sm"] .wire-feature-card__title {
font-size: 1rem;
}
.wire-feature-card[data-size="lg"] .wire-feature-card__title {
font-size: 1.3rem;
}
.wire-feature-card__description {
margin: 0.75rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.9rem;
line-height: 1.65;
text-wrap: pretty;
}
.wire-feature-card[data-size="sm"] .wire-feature-card__description {
margin-top: 0.6rem;
font-size: 0.82rem;
line-height: 1.55;
}
.wire-feature-card[data-size="lg"] .wire-feature-card__description {
font-size: 0.98rem;
}
.wire-feature-card__content {
min-width: 0;
margin-top: 1rem;
}
.wire-feature-card__content:empty {
display: none;
}
.wire-feature-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
min-width: 0;
margin-top: auto;
padding-top: 1.35rem;
}
.wire-feature-card__footer-content {
position: relative;
z-index: 3;
min-width: 0;
}
.wire-feature-card__footer-content:empty {
display: none;
}
.wire-feature-card__action {
position: relative;
z-index: 2;
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
gap: 0.45rem;
min-height: 2.25rem;
margin-left: auto;
color: var(--feature-accent);
font-size: 0.84rem;
font-weight: 600;
line-height: 1.2;
text-decoration: none;
transition:
color 180ms ease,
gap 180ms ease;
}
.wire-feature-card__action::before {
position: absolute;
inset: -0.4rem -0.55rem;
content: "";
border-radius: 0.75rem;
}
.wire-feature-card[data-stretched-link="true"] .wire-feature-card__action::after {
position: absolute;
inset: auto;
z-index: 1;
content: "";
}
.wire-feature-card[data-stretched-link="true"] .wire-feature-card__action {
position: static;
}
.wire-feature-card[data-stretched-link="true"] .wire-feature-card__action::after {
position: absolute;
inset: 0;
border-radius: inherit;
}
.wire-feature-card__action:hover {
color: var(--feature-accent-hover, var(--feature-accent));
gap: 0.65rem;
}
.wire-feature-card__action:focus-visible {
outline: 2px solid var(--wire-color-focus);
outline-offset: 4px;
border-radius: 0.55rem;
}
.wire-feature-card__action-icon {
width: 1rem;
height: 1rem;
transition: transform 180ms ease;
}
.wire-feature-card__action:hover .wire-feature-card__action-icon {
transform: translateX(0.15rem);
}
.wire-feature-card__action--disabled {
color: var(--wire-color-text-muted);
opacity: 0.7;
}
.wire-feature-card[data-align="center"] .wire-feature-card__body {
align-items: center;
text-align: center;
}
.wire-feature-card[data-align="center"] .wire-feature-card__top,
.wire-feature-card[data-align="center"] .wire-feature-card__footer {
width: 100%;
justify-content: center;
}
.wire-feature-card[data-align="center"] .wire-feature-card__action {
margin-inline: auto;
}
.wire-feature-card[data-align="right"] .wire-feature-card__body {
align-items: flex-end;
text-align: right;
}
.wire-feature-card[data-align="right"] .wire-feature-card__top,
.wire-feature-card[data-align="right"] .wire-feature-card__footer {
width: 100%;
justify-content: flex-end;
}
.wire-feature-card[data-variant="raised"] {
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 20px 50px color-mix(in srgb, black 17%, transparent);
}
.wire-feature-card[data-variant="soft"] {
background:
linear-gradient(135deg, var(--feature-accent-soft), transparent 68%),
var(--wire-color-surface-raised);
border-color: color-mix(in srgb, var(--feature-accent) 22%, var(--wire-color-border));
box-shadow: none;
}
.wire-feature-card[data-variant="outline"] {
background: transparent;
box-shadow: none;
}
.wire-feature-card[data-variant="ghost"] {
background: transparent;
border-color: transparent;
box-shadow: none;
}
.wire-feature-card[data-variant="minimal"] {
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-feature-card[data-variant="solid"] {
color: var(--feature-contrast);
background:
linear-gradient(135deg, color-mix(in srgb, white 9%, transparent), transparent 60%),
var(--feature-accent);
border-color: color-mix(in srgb, white 20%, transparent);
box-shadow: 0 22px 54px color-mix(in srgb, var(--feature-accent) 28%, transparent);
}
.wire-feature-card[data-variant="solid"] .wire-feature-card__title,
.wire-feature-card[data-variant="solid"] .wire-feature-card__description,
.wire-feature-card[data-variant="solid"] .wire-feature-card__eyebrow,
.wire-feature-card[data-variant="solid"] .wire-feature-card__action {
color: var(--feature-contrast);
}
.wire-feature-card[data-variant="solid"] .wire-feature-card__description {
opacity: 0.8;
}
.wire-feature-card[data-variant="solid"] .wire-feature-card__icon,
.wire-feature-card[data-variant="solid"] .wire-feature-card__badge {
color: var(--feature-contrast);
background: color-mix(in srgb, white 12%, transparent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-feature-card[data-variant="gradient"] {
background:
radial-gradient(circle at 100% 0%, color-mix(in srgb, var(--feature-accent) 24%, transparent), transparent 44%),
linear-gradient(145deg, var(--wire-color-surface-raised), color-mix(in srgb, var(--feature-accent) 8%, var(--wire-color-surface-raised)));
border-color: color-mix(in srgb, var(--feature-accent) 26%, var(--wire-color-border));
}
.wire-feature-card[data-hover="lift"]:not([data-disabled="true"]):hover {
transform: translateY(-0.3rem);
border-color: color-mix(in srgb, var(--feature-accent) 48%, var(--wire-color-border));
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 58px color-mix(in srgb, black 20%, transparent);
}
.wire-feature-card[data-hover="shadow"]:not([data-disabled="true"]):hover {
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 58px color-mix(in srgb, black 20%, transparent);
}
.wire-feature-card[data-hover="border"]:not([data-disabled="true"]):hover {
border-color: color-mix(in srgb, var(--feature-accent) 58%, var(--wire-color-border));
}
.wire-feature-card[data-hover="glow"]:not([data-disabled="true"]):hover {
border-color: color-mix(in srgb, var(--feature-accent) 54%, var(--wire-color-border));
box-shadow: 0 20px 60px color-mix(in srgb, var(--feature-accent) 22%, transparent);
}
.wire-feature-card:not([data-disabled="true"]):hover .wire-feature-card__image {
transform: scale(1.035);
}
.wire-feature-card:not([data-disabled="true"]):hover .wire-feature-card__title {
color: var(--feature-accent);
}
.wire-feature-card[data-variant="solid"]:not([data-disabled="true"]):hover .wire-feature-card__title {
color: var(--feature-contrast);
}
.wire-feature-card[data-disabled="true"] {
pointer-events: none;
opacity: 0.55;
filter: saturate(0.65);
}
@media (min-width: 48rem) {
.wire-feature-card[data-image-position="left"],
.wire-feature-card[data-image-position="right"] {
display: grid;
grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.12fr);
}
.wire-feature-card[data-image-position="right"] {
grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
}
.wire-feature-card[data-image-position="right"] .wire-feature-card__media {
order: 2;
border-right: 0;
border-bottom: 0;
border-left: 1px solid var(--feature-border);
}
.wire-feature-card[data-image-position="left"] .wire-feature-card__media {
border-right: 1px solid var(--feature-border);
border-bottom: 0;
}
.wire-feature-card[data-image-position="left"] .wire-feature-card__media,
.wire-feature-card[data-image-position="right"] .wire-feature-card__media {
height: 100%;
min-height: 15rem;
aspect-ratio: auto;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-feature-card,
.wire-feature-card__image,
.wire-feature-card__action,
.wire-feature-card__action-icon {
transition: none;
}
}
}
}
+203 -30
View File
@@ -1,7 +1,6 @@
component FeatureGrid {
props {
color = "primary"
size = "default"
items = []
columns = 3
tabletColumns = 2
mobileColumns = 1
@@ -10,46 +9,220 @@ component FeatureGrid {
equalHeight = true
align = "stretch"
maxWidth = "full"
variant = "default"
label = "Features"
class = ""
}
view {
<div
<section
{...attrs}
data-ui-component="FeatureGrid"
data-columns='{columns}'
data-tablet-columns='{tabletColumns}'
data-mobile-columns='{mobileColumns}'
class='grid w-full {class}'
class:wire-feature-grid-autofit='minItemWidth !== ""'
class:grid-cols-1='mobileColumns === 1'
class:grid-cols-2='mobileColumns === 2'
class:sm:grid-cols-1='tabletColumns === 1'
class:sm:grid-cols-2='tabletColumns === 2'
class:sm:grid-cols-3='tabletColumns === 3'
class:lg:grid-cols-2='columns === 2'
class:lg:grid-cols-3='columns === 3'
class:lg:grid-cols-4='columns === 4'
class:lg:grid-cols-5='columns === 5'
class:lg:grid-cols-6='columns === 6'
class:gap-3='gap === "sm"'
class:gap-5='gap === "md"'
class:gap-8='gap === "lg"'
class:gap-10='gap === "xl"'
class:items-stretch='equalHeight || align === "stretch"'
class:items-start='align === "start"'
class:items-center='align === "center"'
class:max-w-5xl='maxWidth === "lg"'
class:max-w-7xl='maxWidth === "xl"'
class:max-w-none='maxWidth === "full"'
style='--wire-feature-grid-min: {minItemWidth || "16rem"};'
data-gap='{gap}'
data-autofit='{minItemWidth !== "" ? "true" : "false"}'
data-equal-height='{equalHeight ? "true" : "false"}'
data-align='{align}'
data-max-width='{maxWidth}'
data-variant='{variant}'
class='wire-feature-grid {class}'
aria-label='{label}'
style='--wire-feature-grid-columns: {columns}; --wire-feature-grid-tablet-columns: {tabletColumns}; --wire-feature-grid-mobile-columns: {mobileColumns}; --wire-feature-grid-min-item-width: {minItemWidth || "17rem"};'
>
<slot></slot>
</div>
<div class="wire-feature-grid__inner">
<div class="wire-feature-grid__grid" role="list">
{#if items.length > 0}
{#each items as item, itemIndex}
<FeatureCard
icon='{item.icon || ""}'
iconStyle='{item.iconStyle || "soft"}'
iconSize='{item.iconSize || "default"}'
eyebrow='{item.eyebrow || ""}'
title='{item.title || item.label || "Feature"}'
description='{item.description || ""}'
image='{item.image || item.imageSrc || ""}'
imageAlt='{item.imageAlt || item.title || item.label || ""}'
imagePosition='{item.imagePosition || "top"}'
imageAspect='{item.imageAspect || "wide"}'
href='{item.href || ""}'
target='{item.target || ""}'
rel='{item.rel || ""}'
external='{item.external || false}'
actionLabel='{item.actionLabel || "Learn more"}'
actionIcon='{item.actionIcon || ""}'
badge='{item.badge || ""}'
badgeColor='{item.badgeColor || item.color || "primary"}'
size='{item.size || "default"}'
color='{item.color || "primary"}'
variant='{item.variant || "default"}'
hover='{item.hover || "lift"}'
align='{item.align || "left"}'
disabled='{item.disabled || false}'
stretchedLink='{item.stretchedLink !== false}'
showArrow='{item.showArrow !== false}'
class='{item.class || ""}'
/>
{/each}
{/if}
<slot></slot>
</div>
</div>
</section>
}
style {
.wire-feature-grid-autofit {
grid-template-columns: repeat(auto-fit, minmax(var(--wire-feature-grid-min), 1fr));
.wire-feature-grid {
--feature-grid-gap: 1.25rem;
position: relative;
width: 100%;
min-width: 0;
color: var(--wire-color-text);
}
.wire-feature-grid[data-gap="none"] {
--feature-grid-gap: 0;
}
.wire-feature-grid[data-gap="xs"] {
--feature-grid-gap: 0.5rem;
}
.wire-feature-grid[data-gap="sm"] {
--feature-grid-gap: 0.75rem;
}
.wire-feature-grid[data-gap="md"] {
--feature-grid-gap: 1.25rem;
}
.wire-feature-grid[data-gap="lg"] {
--feature-grid-gap: 1.75rem;
}
.wire-feature-grid[data-gap="xl"] {
--feature-grid-gap: 2.25rem;
}
.wire-feature-grid__inner {
width: 100%;
margin-inline: auto;
}
.wire-feature-grid[data-max-width="compact"] .wire-feature-grid__inner {
max-width: 64rem;
}
.wire-feature-grid[data-max-width="lg"] .wire-feature-grid__inner {
max-width: 72rem;
}
.wire-feature-grid[data-max-width="xl"] .wire-feature-grid__inner {
max-width: 80rem;
}
.wire-feature-grid[data-max-width="wide"] .wire-feature-grid__inner {
max-width: 90rem;
}
.wire-feature-grid[data-max-width="2xl"] .wire-feature-grid__inner {
max-width: 96rem;
}
.wire-feature-grid[data-max-width="full"] .wire-feature-grid__inner {
max-width: none;
}
.wire-feature-grid__grid {
display: grid;
grid-template-columns: repeat(
var(--wire-feature-grid-mobile-columns),
minmax(0, 1fr)
);
align-items: stretch;
gap: var(--feature-grid-gap);
width: 100%;
min-width: 0;
}
.wire-feature-grid[data-align="start"] .wire-feature-grid__grid {
align-items: start;
}
.wire-feature-grid[data-align="center"] .wire-feature-grid__grid {
align-items: center;
}
.wire-feature-grid[data-align="end"] .wire-feature-grid__grid {
align-items: end;
}
.wire-feature-grid__grid > * {
min-width: 0;
max-width: 100%;
}
.wire-feature-grid[data-equal-height="true"] .wire-feature-grid__grid > * {
height: 100%;
}
.wire-feature-grid[data-variant="panel"] .wire-feature-grid__inner {
padding: 1rem;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 1.5rem;
}
.wire-feature-grid[data-variant="soft"] .wire-feature-grid__inner {
padding: 1rem;
background: color-mix(in srgb, var(--wire-color-primary) 5%, transparent);
border: 1px solid color-mix(in srgb, var(--wire-color-primary) 14%, var(--wire-color-border));
border-radius: 1.5rem;
}
@media (min-width: 40rem) {
.wire-feature-grid__grid {
grid-template-columns: repeat(
var(--wire-feature-grid-tablet-columns),
minmax(0, 1fr)
);
}
.wire-feature-grid[data-autofit="true"] .wire-feature-grid__grid {
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, var(--wire-feature-grid-min-item-width)), 1fr)
);
}
.wire-feature-grid[data-variant="panel"] .wire-feature-grid__inner,
.wire-feature-grid[data-variant="soft"] .wire-feature-grid__inner {
padding: 1.25rem;
}
}
@media (min-width: 64rem) {
.wire-feature-grid__grid {
grid-template-columns: repeat(
var(--wire-feature-grid-columns),
minmax(0, 1fr)
);
}
.wire-feature-grid[data-autofit="true"] .wire-feature-grid__grid {
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, var(--wire-feature-grid-min-item-width)), 1fr)
);
}
.wire-feature-grid[data-variant="panel"] .wire-feature-grid__inner,
.wire-feature-grid[data-variant="soft"] .wire-feature-grid__inner {
padding: 1.5rem;
}
}
}
}
File diff suppressed because it is too large Load Diff
+627 -65
View File
@@ -1,107 +1,669 @@
component MetricCard {
props {
@event select = function
@event action = function
label = "Metric"
value = "0"
description = ""
icon = ""
iconStyle = "soft"
prefix = ""
suffix = ""
badge = ""
trend = ""
trendLabel = ""
trendDirection = "neutral"
progress = -1
progressLabel = ""
href = ""
target = ""
rel = ""
external = false
actionLabel = ""
actionIcon = ""
showArrow = true
selectable = false
disabled = false
size = "default"
color = "primary"
variant = "default"
hover = "lift"
align = "left"
class = ""
}
functions {
function selectCard(sourceEvent) {
if (disabled || !selectable) {
return
}
if (sourceEvent.target.closest(".wire-metric-card__action")) {
return
}
$emit("select", {
label: label,
value: value,
href: href,
sourceEvent: sourceEvent
})
}
function activateAction(sourceEvent) {
if (disabled) {
sourceEvent.preventDefault()
return
}
$emit("action", {
label: label,
value: value,
href: href,
sourceEvent: sourceEvent
})
}
}
view {
<article
{...attrs}
data-ui-component="MetricCard"
class='group relative flex h-full flex-col rounded-2xl border border-[var(--wire-color-border)] bg-[var(--wire-color-surface-raised)] p-5 shadow-sm transition {class}'
class:p-4='size === "sm"'
class:p-6='size === "lg"'
class:bg-[var(--wire-color-primary-soft)]='variant === "soft" && color === "primary"'
class:bg-[var(--wire-color-success-soft)]='variant === "soft" && color === "success"'
class:bg-[var(--wire-color-warning-soft)]='variant === "soft" && color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='variant === "soft" && color === "danger"'
class:bg-transparent='variant === "minimal"'
class:border-transparent='variant === "minimal"'
class:shadow-none='variant === "minimal"'
class:text-center='align === "center"'
class:items-center='align === "center"'
class:text-right='align === "right"'
class:items-end='align === "right"'
class:hover:border-[var(--wire-color-primary)]='href !== ""'
class:hover:shadow-lg='href !== ""'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-hover='{hover}'
data-align='{align}'
data-icon-style='{iconStyle}'
data-disabled='{disabled ? "true" : "false"}'
data-selectable='{selectable ? "true" : "false"}'
class='wire-metric-card {class}'
role='{selectable ? "button" : ""}'
tabindex='{selectable && !disabled ? "0" : "-1"}'
@click='selectCard(event)'
@keydown='if (selectable && (event.key === "Enter" || event.key === " ")) { event.preventDefault(); selectCard(event) }'
style='--wire-metric-progress: {progress}%;'
>
<div class="flex w-full items-start justify-between gap-4">
<div>
<p class="text-sm font-semibold text-[var(--wire-color-text-muted)]">{label}</p>
<p class="mt-2 text-3xl font-bold tracking-tight text-[var(--wire-color-text)]" class:text-4xl='size === "lg"'>
<span>{prefix}</span><span>{value}</span><span>{suffix}</span>
</p>
<div class="wire-metric-card__top">
<div class="wire-metric-card__heading">
{#if label}
<p class="wire-metric-card__label">{label}</p>
{/if}
{#if badge}
<span class="wire-metric-card__badge">{badge}</span>
{/if}
</div>
{#if icon}
<span
class="flex size-11 shrink-0 items-center justify-center rounded-2xl bg-[var(--wire-color-primary-soft)] text-[var(--wire-color-primary)]"
class:bg-[var(--wire-color-success-soft)]='color === "success"'
class:text-[var(--wire-color-success)]='color === "success"'
class:bg-[var(--wire-color-warning-soft)]='color === "warning"'
class:text-[var(--wire-color-warning-text)]='color === "warning"'
class:bg-[var(--wire-color-danger-soft)]='color === "danger"'
class:text-[var(--wire-color-danger)]='color === "danger"'
class:bg-[var(--wire-color-info-soft)]='color === "info"'
class:text-[var(--wire-color-info)]='color === "info"'
>
<span class='{icon + " size-5"}' aria-hidden="true"></span>
</span>
<div class="wire-metric-card__icon" aria-hidden="true">
<span class='{icon}'></span>
</div>
{/if}
</div>
<div class="wire-metric-card__value-row">
{#if prefix}
<span class="wire-metric-card__prefix">{prefix}</span>
{/if}
<strong class="wire-metric-card__value">{value}</strong>
{#if suffix}
<span class="wire-metric-card__suffix">{suffix}</span>
{/if}
</div>
{#if description}
<p class="mt-3 text-sm leading-6 text-[var(--wire-color-text-muted)]">{description}</p>
<p class="wire-metric-card__description">{description}</p>
{/if}
<slot></slot>
{#if progress >= 0}
<div class="wire-metric-card__progress-block">
<div class="wire-metric-card__progress-meta">
<span>{progressLabel || "Progress"}</span>
<strong>{progress}%</strong>
</div>
<div
class="wire-metric-card__progress-track"
role="progressbar"
aria-label='{progressLabel || label}'
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow='{progress}'
>
<span class="wire-metric-card__progress-value"></span>
</div>
</div>
{/if}
{#if trend || trendLabel}
<div class="mt-4 inline-flex items-center gap-2 text-sm font-semibold">
<span
class="inline-flex items-center gap-1 rounded-full px-2.5 py-1"
class:bg-[var(--wire-color-success-soft)]='trendDirection === "up" || trendDirection === "positive"'
class:text-[var(--wire-color-success)]='trendDirection === "up" || trendDirection === "positive"'
class:bg-[var(--wire-color-danger-soft)]='trendDirection === "down" || trendDirection === "negative"'
class:text-[var(--wire-color-danger)]='trendDirection === "down" || trendDirection === "negative"'
class:bg-[var(--wire-color-surface-soft)]='trendDirection === "neutral"'
class:text-[var(--wire-color-text-muted)]='trendDirection === "neutral"'
>
{#if trendDirection === "up" || trendDirection === "positive"}
<span class="icon-[lucide--trending-up] size-4" aria-hidden="true"></span>
{:else if trendDirection === "down" || trendDirection === "negative"}
<span class="icon-[lucide--trending-down] size-4" aria-hidden="true"></span>
{:else}
<span class="icon-[lucide--minus] size-4" aria-hidden="true"></span>
{/if}
<span>{trend}</span>
</span>
<div
class="wire-metric-card__trend"
data-direction='{trendDirection}'
>
{#if trendDirection === "up" || trendDirection === "positive"}
<span class="icon-[lucide--trending-up] wire-metric-card__trend-icon" aria-hidden="true"></span>
{:else if trendDirection === "down" || trendDirection === "negative"}
<span class="icon-[lucide--trending-down] wire-metric-card__trend-icon" aria-hidden="true"></span>
{:else}
<span class="icon-[lucide--minus] wire-metric-card__trend-icon" aria-hidden="true"></span>
{/if}
{#if trend}
<strong class="wire-metric-card__trend-value">{trend}</strong>
{/if}
{#if trendLabel}
<span class="text-[var(--wire-color-text-muted)]">{trendLabel}</span>
<span class="wire-metric-card__trend-label">{trendLabel}</span>
{/if}
</div>
{/if}
{#if href}
<TextLink
label='{actionLabel || "View details"}'
href='{href}'
color='{color}'
size="sm"
class="mt-5"
/>
{/if}
<div class="wire-metric-card__footer">
<slot name="footer"></slot>
{#if href}
<a
href='{href}'
target='{target}'
rel='{external || target === "_blank" ? "noopener noreferrer" : rel}'
class="wire-metric-card__action"
aria-disabled='{disabled ? "true" : "false"}'
@click='activateAction(event)'
>
<span>{actionLabel || "View details"}</span>
{#if actionIcon}
<span class='wire-metric-card__action-icon {actionIcon}' aria-hidden="true"></span>
{:else if external}
<span class="wire-metric-card__action-icon icon-[lucide--external-link]" aria-hidden="true"></span>
{:else if showArrow}
<span class="wire-metric-card__action-icon icon-[lucide--arrow-right]" aria-hidden="true"></span>
{/if}
</a>
{:else if actionLabel}
<button
type="button"
class="wire-metric-card__action"
disabled='{disabled}'
@click='activateAction(event)'
>
<span>{actionLabel}</span>
{#if actionIcon}
<span class='wire-metric-card__action-icon {actionIcon}' aria-hidden="true"></span>
{:else if showArrow}
<span class="wire-metric-card__action-icon icon-[lucide--arrow-right]" aria-hidden="true"></span>
{/if}
</button>
{/if}
</div>
</article>
}
style {
.wire-metric-card {
--metric-accent: var(--wire-color-primary);
--metric-accent-hover: var(--wire-color-primary-hover);
--metric-accent-soft: var(--wire-color-primary-soft);
--metric-accent-muted: var(--wire-color-primary-muted);
--metric-contrast: var(--wire-color-primary-contrast);
--metric-border: color-mix(in srgb, var(--metric-accent) 16%, var(--wire-color-border));
position: relative;
isolation: isolate;
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
height: 100%;
padding: 1.5rem;
color: var(--wire-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--metric-accent) 8%, transparent),
transparent 36%
),
var(--wire-color-surface-raised);
border: 1px solid var(--metric-border);
border-radius: 1.25rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 4%, transparent) inset,
0 14px 36px color-mix(in srgb, black 10%, transparent);
overflow: hidden;
transition:
transform 180ms ease,
border-color 180ms ease,
box-shadow 180ms ease,
background-color 180ms ease;
}
.wire-metric-card[data-color="secondary"] {
--metric-accent: var(--wire-color-secondary);
--metric-accent-hover: var(--wire-color-secondary-hover);
--metric-accent-soft: var(--wire-color-secondary-soft);
--metric-accent-muted: var(--wire-color-secondary-muted);
--metric-contrast: var(--wire-color-secondary-contrast);
}
.wire-metric-card[data-color="info"] {
--metric-accent: var(--wire-color-info);
--metric-accent-hover: var(--wire-color-info-hover);
--metric-accent-soft: var(--wire-color-info-soft);
--metric-accent-muted: var(--wire-color-info-muted);
--metric-contrast: var(--wire-color-info-contrast);
}
.wire-metric-card[data-color="success"] {
--metric-accent: var(--wire-color-success);
--metric-accent-hover: var(--wire-color-success-hover);
--metric-accent-soft: var(--wire-color-success-soft);
--metric-accent-muted: var(--wire-color-success-muted);
--metric-contrast: var(--wire-color-success-contrast);
}
.wire-metric-card[data-color="warning"] {
--metric-accent: var(--wire-color-warning);
--metric-accent-hover: var(--wire-color-warning-hover);
--metric-accent-soft: var(--wire-color-warning-soft);
--metric-accent-muted: var(--wire-color-warning-muted);
--metric-contrast: var(--wire-color-warning-contrast);
}
.wire-metric-card[data-color="danger"] {
--metric-accent: var(--wire-color-danger);
--metric-accent-hover: var(--wire-color-danger-hover);
--metric-accent-soft: var(--wire-color-danger-soft);
--metric-accent-muted: var(--wire-color-danger-muted);
--metric-contrast: var(--wire-color-danger-contrast);
}
.wire-metric-card[data-size="sm"] {
padding: 1.1rem;
border-radius: 1rem;
}
.wire-metric-card[data-size="lg"] {
padding: 1.9rem;
border-radius: 1.5rem;
}
.wire-metric-card[data-variant="soft"] {
background:
linear-gradient(135deg, var(--metric-accent-soft), transparent 68%),
var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-metric-card[data-variant="raised"] {
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 64px color-mix(in srgb, black 16%, transparent);
}
.wire-metric-card[data-variant="outline"] {
background: transparent;
box-shadow: none;
}
.wire-metric-card[data-variant="minimal"] {
padding-inline: 0;
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-metric-card[data-variant="solid"] {
color: var(--metric-contrast);
background:
linear-gradient(135deg, color-mix(in srgb, white 10%, transparent), transparent 58%),
var(--metric-accent);
border-color: color-mix(in srgb, white 22%, transparent);
box-shadow: 0 20px 52px color-mix(in srgb, var(--metric-accent) 28%, transparent);
}
.wire-metric-card[data-variant="gradient"] {
background:
linear-gradient(
145deg,
color-mix(in srgb, var(--metric-accent) 20%, var(--wire-color-surface-raised)),
var(--wire-color-surface-raised) 58%
);
}
.wire-metric-card[data-disabled="true"] {
cursor: not-allowed;
opacity: 0.58;
pointer-events: none;
}
.wire-metric-card[data-selectable="true"]:not([data-disabled="true"]) {
cursor: pointer;
}
.wire-metric-card[data-hover="lift"]:hover {
transform: translateY(-4px);
border-color: color-mix(in srgb, var(--metric-accent) 42%, var(--wire-color-border));
box-shadow: 0 24px 56px color-mix(in srgb, black 16%, transparent);
}
.wire-metric-card[data-hover="border"]:hover {
border-color: var(--metric-accent);
}
.wire-metric-card[data-hover="glow"]:hover {
border-color: color-mix(in srgb, var(--metric-accent) 52%, var(--wire-color-border));
box-shadow: 0 22px 58px color-mix(in srgb, var(--metric-accent) 18%, transparent);
}
.wire-metric-card[data-align="center"] {
align-items: center;
text-align: center;
}
.wire-metric-card[data-align="right"] {
align-items: flex-end;
text-align: right;
}
.wire-metric-card__top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
width: 100%;
}
.wire-metric-card__heading {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.55rem;
min-width: 0;
}
.wire-metric-card__label {
margin: 0;
color: var(--wire-color-text-muted);
font-size: 0.84rem;
font-weight: 600;
line-height: 1.4;
}
.wire-metric-card__badge {
display: inline-flex;
align-items: center;
min-height: 1.45rem;
padding: 0.2rem 0.55rem;
color: var(--metric-accent);
background: var(--metric-accent-soft);
border: 1px solid color-mix(in srgb, var(--metric-accent) 24%, transparent);
border-radius: 999px;
font-size: 0.69rem;
font-weight: 700;
line-height: 1;
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__label,
.wire-metric-card[data-variant="solid"] .wire-metric-card__description,
.wire-metric-card[data-variant="solid"] .wire-metric-card__trend-label,
.wire-metric-card[data-variant="solid"] .wire-metric-card__progress-meta {
color: color-mix(in srgb, currentColor 78%, transparent);
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__badge {
color: currentColor;
background: color-mix(in srgb, white 14%, transparent);
border-color: color-mix(in srgb, white 22%, transparent);
}
.wire-metric-card__icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.75rem;
height: 2.75rem;
color: var(--metric-accent);
background: var(--metric-accent-soft);
border: 1px solid color-mix(in srgb, var(--metric-accent) 18%, transparent);
border-radius: 0.9rem;
font-size: 1.25rem;
}
.wire-metric-card[data-icon-style="ghost"] .wire-metric-card__icon {
background: transparent;
border-color: transparent;
}
.wire-metric-card[data-icon-style="outline"] .wire-metric-card__icon {
background: transparent;
border-color: color-mix(in srgb, var(--metric-accent) 34%, var(--wire-color-border));
}
.wire-metric-card[data-icon-style="solid"] .wire-metric-card__icon,
.wire-metric-card[data-variant="solid"] .wire-metric-card__icon {
color: var(--metric-contrast);
background: var(--metric-accent);
border-color: transparent;
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__icon {
color: currentColor;
background: color-mix(in srgb, white 14%, transparent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-metric-card__value-row {
display: flex;
align-items: baseline;
gap: 0.2rem;
width: 100%;
margin-top: 1.15rem;
}
.wire-metric-card[data-align="center"] .wire-metric-card__value-row {
justify-content: center;
}
.wire-metric-card[data-align="right"] .wire-metric-card__value-row {
justify-content: flex-end;
}
.wire-metric-card__value {
color: var(--wire-color-text);
font-size: clamp(2rem, 5vw, 2.8rem);
font-weight: 700;
line-height: 0.98;
letter-spacing: -0.045em;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.wire-metric-card[data-size="sm"] .wire-metric-card__value {
font-size: clamp(1.65rem, 4vw, 2.15rem);
}
.wire-metric-card[data-size="lg"] .wire-metric-card__value {
font-size: clamp(2.45rem, 6vw, 3.5rem);
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__value {
color: currentColor;
}
.wire-metric-card__prefix,
.wire-metric-card__suffix {
color: var(--metric-accent);
font-size: 1rem;
font-weight: 700;
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__prefix,
.wire-metric-card[data-variant="solid"] .wire-metric-card__suffix {
color: currentColor;
}
.wire-metric-card__description {
max-width: 38rem;
margin: 0.8rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.86rem;
line-height: 1.7;
}
.wire-metric-card__progress-block {
width: 100%;
margin-top: 1.25rem;
}
.wire-metric-card__progress-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-bottom: 0.45rem;
color: var(--wire-color-text-muted);
font-size: 0.75rem;
}
.wire-metric-card__progress-track {
width: 100%;
height: 0.42rem;
overflow: hidden;
background: color-mix(in srgb, var(--wire-color-border) 72%, transparent);
border-radius: 999px;
}
.wire-metric-card__progress-value {
display: block;
width: clamp(0%, var(--wire-metric-progress), 100%);
height: 100%;
background: var(--metric-accent);
border-radius: inherit;
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__progress-track {
background: color-mix(in srgb, white 20%, transparent);
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__progress-value {
background: currentColor;
}
.wire-metric-card__trend {
display: inline-flex;
align-items: center;
align-self: flex-start;
gap: 0.42rem;
margin-top: 1.15rem;
color: var(--wire-color-text-muted);
font-size: 0.76rem;
line-height: 1.4;
}
.wire-metric-card[data-align="center"] .wire-metric-card__trend {
align-self: center;
}
.wire-metric-card[data-align="right"] .wire-metric-card__trend {
align-self: flex-end;
}
.wire-metric-card__trend[data-direction="up"],
.wire-metric-card__trend[data-direction="positive"] {
color: var(--wire-color-success);
}
.wire-metric-card__trend[data-direction="down"],
.wire-metric-card__trend[data-direction="negative"] {
color: var(--wire-color-danger);
}
.wire-metric-card__trend-icon {
width: 0.95rem;
height: 0.95rem;
}
.wire-metric-card__trend-label {
color: var(--wire-color-text-muted);
font-weight: 500;
}
.wire-metric-card__footer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
width: 100%;
margin-top: auto;
padding-top: 1.35rem;
}
.wire-metric-card__action {
appearance: none;
padding: 0;
background: transparent;
border: 0;
font: inherit;
cursor: pointer;
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
gap: 0.45rem;
color: var(--metric-accent);
font-size: 0.82rem;
font-weight: 700;
line-height: 1.3;
text-decoration: none;
transition:
color 160ms ease,
gap 160ms ease;
}
.wire-metric-card__action:hover {
gap: 0.62rem;
color: var(--metric-accent-hover);
}
.wire-metric-card[data-variant="solid"] .wire-metric-card__action {
color: currentColor;
}
.wire-metric-card__action-icon {
width: 1rem;
height: 1rem;
}
.wire-metric-card__action:focus-visible {
outline: 2px solid var(--wire-color-focus);
outline-offset: 4px;
border-radius: 0.35rem;
}
@media (max-width: 639px) {
.wire-metric-card {
padding: 1.2rem;
}
.wire-metric-card__value {
font-size: 2.05rem;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-metric-card,
.wire-metric-card__action {
transition: none;
}
.wire-metric-card[data-hover="lift"]:hover {
transform: none;
}
}
}
}
+209 -40
View File
@@ -1,5 +1,8 @@
component MetricGrid {
props {
@event select = function
@event action = function
items = []
columns = 4
tabletColumns = 2
@@ -10,51 +13,217 @@ component MetricGrid {
size = "default"
color = "primary"
variant = "default"
maxWidth = "full"
minItemWidth = ""
class = ""
}
functions {
function forwardSelect(item, itemIndex, sourceEvent) {
$emit("select", {
item: item,
itemIndex: itemIndex,
sourceEvent: sourceEvent
})
}
function forwardAction(item, itemIndex, sourceEvent) {
$emit("action", {
item: item,
itemIndex: itemIndex,
sourceEvent: sourceEvent
})
}
}
view {
<div
<section
{...attrs}
data-ui-component="MetricGrid"
class='grid w-full grid-cols-1 {class}'
class:grid-cols-2='mobileColumns === 2'
class:sm:grid-cols-2='tabletColumns === 2'
class:sm:grid-cols-3='tabletColumns === 3'
class:lg:grid-cols-2='columns === 2'
class:lg:grid-cols-3='columns === 3'
class:lg:grid-cols-4='columns === 4'
class:lg:grid-cols-5='columns === 5'
class:lg:grid-cols-6='columns === 6'
class:gap-3='gap === "sm"'
class:gap-5='gap === "md"'
class:gap-8='gap === "lg"'
class:divide-y='dividers'
class:sm:divide-y-0='dividers'
class:sm:divide-x='dividers'
class:divide-[var(--wire-color-border)]='dividers'
data-gap='{gap}'
data-equal-height='{equalHeight ? "true" : "false"}'
data-dividers='{dividers ? "true" : "false"}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-max-width='{maxWidth}'
data-auto-fit='{minItemWidth ? "true" : "false"}'
class='wire-metric-grid {class}'
style='display:block;width:100%;min-width:0;--wire-metric-columns:{columns};--wire-metric-tablet-columns:{tabletColumns};--wire-metric-mobile-columns:{mobileColumns};--wire-metric-min-item-width:{minItemWidth || "18rem"};'
>
{#each items as item}
<MetricCard
label='{item.label || item.title}'
value='{item.value}'
description='{item.description || ""}'
icon='{item.icon || ""}'
prefix='{item.prefix || ""}'
suffix='{item.suffix || ""}'
trend='{item.trend || ""}'
trendLabel='{item.trendLabel || ""}'
trendDirection='{item.trendDirection || "neutral"}'
href='{item.href || ""}'
actionLabel='{item.actionLabel || ""}'
size='{item.size || size}'
color='{item.color || color}'
variant='{dividers ? "minimal" : (item.variant || variant)}'
align='{item.align || "left"}'
class="h-full"
/>
{:empty}
<slot></slot>
{/each}
</div>
<div class="wire-metric-grid__frame">
<div class="wire-metric-grid__surface">
{#each items as item, itemIndex}
<MetricCard
label='{item.label || item.title || "Metric"}'
value='{item.value}'
description='{item.description || ""}'
icon='{item.icon || ""}'
iconStyle='{item.iconStyle || "soft"}'
prefix='{item.prefix || ""}'
suffix='{item.suffix || ""}'
badge='{item.badge || ""}'
trend='{item.trend || ""}'
trendLabel='{item.trendLabel || ""}'
trendDirection='{item.trendDirection || "neutral"}'
progress='{item.progress === undefined ? -1 : item.progress}'
progressLabel='{item.progressLabel || ""}'
href='{item.href || ""}'
target='{item.target || ""}'
rel='{item.rel || ""}'
external='{item.external || false}'
actionLabel='{item.actionLabel || ""}'
actionIcon='{item.actionIcon || ""}'
showArrow='{item.showArrow === undefined ? true : item.showArrow}'
selectable='{item.selectable || false}'
disabled='{item.disabled || false}'
size='{item.size || size}'
color='{item.color || color}'
variant='{dividers ? "minimal" : (item.variant || variant)}'
hover='{item.hover || (dividers ? "none" : "lift")}'
align='{item.align || "left"}'
class="wire-metric-grid__card"
@select='forwardSelect(item, itemIndex, event)'
@action='forwardAction(item, itemIndex, event)'
/>
{:empty}
<slot></slot>
{/each}
</div>
</div>
</section>
}
style {
.wire-metric-grid__frame {
width: 100%;
min-width: 0;
margin-inline: auto;
}
.wire-metric-grid[data-max-width="compact"] .wire-metric-grid__frame {
max-width: 64rem;
}
.wire-metric-grid[data-max-width="lg"] .wire-metric-grid__frame {
max-width: 72rem;
}
.wire-metric-grid[data-max-width="xl"] .wire-metric-grid__frame {
max-width: 80rem;
}
.wire-metric-grid[data-max-width="wide"] .wire-metric-grid__frame,
.wire-metric-grid[data-max-width="2xl"] .wire-metric-grid__frame {
max-width: 90rem;
}
.wire-metric-grid[data-max-width="full"] .wire-metric-grid__frame {
max-width: none;
}
.wire-metric-grid__surface {
display: grid;
grid-template-columns: repeat(var(--wire-metric-mobile-columns), minmax(0, 1fr));
gap: 1.25rem;
width: 100%;
min-width: 0;
align-items: stretch;
}
.wire-metric-grid[data-auto-fit="true"] .wire-metric-grid__surface {
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, var(--wire-metric-min-item-width)), 1fr)
);
}
.wire-metric-grid[data-gap="none"] .wire-metric-grid__surface {
gap: 0;
}
.wire-metric-grid[data-gap="sm"] .wire-metric-grid__surface {
gap: 0.75rem;
}
.wire-metric-grid[data-gap="lg"] .wire-metric-grid__surface {
gap: 1.75rem;
}
.wire-metric-grid[data-gap="xl"] .wire-metric-grid__surface {
gap: 2.25rem;
}
.wire-metric-grid[data-equal-height="true"] .wire-metric-grid__surface > * {
min-width: 0;
height: 100%;
}
.wire-metric-grid[data-variant="panel"] .wire-metric-grid__surface,
.wire-metric-grid[data-variant="soft"] .wire-metric-grid__surface,
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface {
overflow: hidden;
padding: 0.75rem;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 1.4rem;
}
.wire-metric-grid[data-variant="soft"] .wire-metric-grid__surface {
background:
linear-gradient(
135deg,
color-mix(in srgb, var(--wire-color-primary) 8%, transparent),
transparent 62%
),
var(--wire-color-surface-raised);
}
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface {
gap: 0;
padding: 0;
}
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__card {
border-radius: 0;
}
@media (min-width: 640px) {
.wire-metric-grid[data-auto-fit="false"] .wire-metric-grid__surface {
grid-template-columns: repeat(var(--wire-metric-tablet-columns), minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.wire-metric-grid[data-auto-fit="false"] .wire-metric-grid__surface {
grid-template-columns: repeat(var(--wire-metric-columns), minmax(0, 1fr));
}
}
@media (max-width: 639px) {
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface > * + * {
border-top: 1px solid var(--wire-color-border);
}
}
@media (min-width: 640px) and (max-width: 1023px) {
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface > * {
border-top: 1px solid var(--wire-color-border);
}
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface > *:nth-child(-n + 2) {
border-top: 0;
}
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface > *:nth-child(2n) {
border-left: 1px solid var(--wire-color-border);
}
}
@media (min-width: 1024px) {
.wire-metric-grid[data-dividers="true"] .wire-metric-grid__surface > * + * {
border-left: 1px solid var(--wire-color-border);
}
}
}
}
+591 -9
View File
@@ -4,20 +4,602 @@ component Modal {
@event close = function
@event cancel = function
@event confirm = function
size = "default"
color = "primary"
isOpen = false
defaultOpen = false
title = "Modal"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
icon = ""
label = "Modal dialog"
size = "md"
placement = "center"
color = "primary"
variant = "default"
showClose = true
closeLabel = "Close modal"
closeOnBackdrop = true
closeOnEscape = true
closeOnCancel = true
closeOnConfirm = false
showFooter = true
cancelLabel = "Cancel"
cancelIcon = ""
confirmLabel = "Confirm"
confirmIcon = ""
confirmDisabled = false
confirmLoading = false
destructive = false
triggerLabel = ""
triggerIcon = ""
scrollable = true
class = ""
}
state visible = defaultOpen
functions {
function isOpen() {
return open || visible
}
function showModal(sourceEvent) {
visible = true
$emit("open", { sourceEvent: sourceEvent })
}
function hideModal(reason, sourceEvent) {
visible = false
$emit("close", {
reason: reason,
sourceEvent: sourceEvent
})
}
function cancelModal(sourceEvent) {
$emit("cancel", { sourceEvent: sourceEvent })
if (closeOnCancel) {
hideModal("cancel", sourceEvent)
}
}
function confirmModal(sourceEvent) {
if (confirmDisabled || confirmLoading) {
return
}
$emit("confirm", { sourceEvent: sourceEvent })
if (closeOnConfirm) {
hideModal("confirm", sourceEvent)
}
}
function handleKeydown(sourceEvent) {
if (closeOnEscape && sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
cancelModal(sourceEvent)
}
}
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--modal wire-next--placement-{placement} {class}" role="dialog" aria-modal="true" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
<div
{...attrs}
data-ui-component="Modal"
data-open='{open || visible ? "true" : "false"}'
data-size='{size}'
data-placement='{placement}'
data-color='{color}'
data-variant='{variant}'
data-scrollable='{scrollable ? "true" : "false"}'
data-destructive='{destructive ? "true" : "false"}'
class='wire-modal {class}'
>
{#if triggerLabel}
<button
type="button"
class="wire-modal__trigger"
aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}'
@click='showModal(event)'
>
{#if triggerIcon}
<span
class='{triggerIcon}'
aria-hidden="true"
>
</span>
{/if}
<span>{triggerLabel}</span>
</button>
{/if}
<span
class="wire-modal__trigger-slot"
@click='showModal(event)'
>
<slot
name="trigger"
>
</slot>
</span>
<div
class="wire-modal__layer"
data-show='{open || visible}'
role="presentation"
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-modal__backdrop"
aria-label='{closeLabel}'
@click='if (closeOnBackdrop) { cancelModal(event) }'
>
</button>
<section
class="wire-modal__panel"
role="dialog"
aria-modal="true"
aria-label='{label || title}'
tabindex="-1"
>
<header
class="wire-modal__header"
>
<div
class="wire-modal__heading"
>
{#if icon}
<span
class='wire-modal__icon {icon}'
aria-hidden="true"
>
</span>
{/if}
<div
class="wire-modal__heading-copy"
>
<slot
name="header"
>
</slot>
{#if title}
<h2>{title}</h2>
{/if}
{#if description}
<p>{description}</p>
{/if}
</div>
</div>
{#if showClose}
<button
type="button"
class="wire-modal__close"
aria-label='{closeLabel}'
@click='hideModal("close-button", event)'
>
<span
class="icon-[lucide--x]"
aria-hidden="true"
>
</span>
</button>
{/if}
</header>
<div
class="wire-modal__body"
>
<slot></slot>
</div>
{#if showFooter}
<footer
class="wire-modal__footer"
>
<div
class="wire-modal__custom-footer"
>
<slot
name="footer"
>
</slot>
</div>
<div
class="wire-modal__actions"
>
{#if cancelLabel}
<button
type="button"
class="wire-modal__button wire-modal__button--secondary"
@click='cancelModal(event)'
>
{#if cancelIcon}
<span
class='{cancelIcon}'
aria-hidden="true"
>
</span>
{/if}
<span>{cancelLabel}</span>
</button>
{/if}
{#if confirmLabel}
<button
type="button"
class="wire-modal__button wire-modal__button--primary"
disabled='{confirmDisabled || confirmLoading}'
@click='confirmModal(event)'
>
{#if confirmLoading}
<span
class="wire-modal__spinner"
aria-hidden="true"
>
</span>
{:else if confirmIcon}
<span
class='{confirmIcon}'
aria-hidden="true"
>
</span>
{/if}
<span>{confirmLabel}</span>
</button>
{/if}
</div>
</footer>
{/if}
</section>
</div>
</div>
}
style {
.wire-modal {
--modal-accent: var(--wire-color-primary);
--modal-soft: var(--wire-color-primary-soft);
--modal-contrast: var(--wire-color-primary-contrast);
position: relative;
display: inline-flex;
}
.wire-modal[data-color="secondary"] {
--modal-accent: var(--wire-color-secondary);
--modal-soft: var(--wire-color-secondary-soft);
--modal-contrast: var(--wire-color-secondary-contrast);
}
.wire-modal[data-color="info"] {
--modal-accent: var(--wire-color-info);
--modal-soft: var(--wire-color-info-soft);
--modal-contrast: var(--wire-color-info-contrast);
}
.wire-modal[data-color="success"] {
--modal-accent: var(--wire-color-success);
--modal-soft: var(--wire-color-success-soft);
--modal-contrast: var(--wire-color-success-contrast);
}
.wire-modal[data-color="warning"] {
--modal-accent: var(--wire-color-warning);
--modal-soft: var(--wire-color-warning-soft);
--modal-contrast: var(--wire-color-warning-text);
}
.wire-modal[data-color="danger"],
.wire-modal[data-destructive="true"] {
--modal-accent: var(--wire-color-danger);
--modal-soft: var(--wire-color-danger-soft);
--modal-contrast: var(--wire-color-on-danger);
}
.wire-modal__trigger,
.wire-modal__trigger-slot {
display: inline-flex;
align-items: center;
gap: 0.55rem;
}
.wire-modal__trigger {
appearance: none;
min-height: 2.55rem;
padding: 0.65rem 1rem;
color: var(--modal-contrast);
background: var(--modal-accent);
border: 0;
border-radius: 0.8rem;
font: inherit;
font-size: 0.85rem;
font-weight: 650;
cursor: pointer;
}
.wire-modal__layer {
position: fixed;
inset: 0;
z-index: 1250;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.wire-modal[data-placement="top"] .wire-modal__layer {
align-items: flex-start;
padding-top: clamp(1rem, 8vh, 5rem);
}
.wire-modal__backdrop {
position: absolute;
inset: 0;
z-index: 0;
appearance: none;
padding: 0;
background: color-mix(in srgb, black 62%, transparent);
border: 0;
backdrop-filter: blur(9px);
}
.wire-modal__panel {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
width: min(32rem, calc(100vw - 2rem));
max-height: min(88vh, 52rem);
color: var(--wire-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--modal-accent) 9%, transparent),
transparent 34%
),
var(--wire-color-surface-raised);
border: 1px solid color-mix(in srgb, var(--modal-accent) 18%, var(--wire-color-border));
border-radius: 1.35rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 40px 110px color-mix(in srgb, black 38%, transparent);
overflow: hidden;
}
.wire-modal[data-size="sm"] .wire-modal__panel {
width: min(25rem, calc(100vw - 2rem));
}
.wire-modal[data-size="lg"] .wire-modal__panel {
width: min(44rem, calc(100vw - 2rem));
}
.wire-modal[data-size="xl"] .wire-modal__panel {
width: min(64rem, calc(100vw - 2rem));
}
.wire-modal[data-size="full"] .wire-modal__panel {
width: calc(100vw - 2rem);
height: calc(100vh - 2rem);
max-height: none;
}
.wire-modal[data-variant="soft"] .wire-modal__panel {
background:
linear-gradient(145deg, var(--modal-soft), transparent 68%),
var(--wire-color-surface-raised);
}
.wire-modal[data-variant="outline"] .wire-modal__panel {
background: var(--wire-color-surface-raised);
box-shadow: 0 24px 72px color-mix(in srgb, black 24%, transparent);
}
.wire-modal[data-variant="solid"] .wire-modal__panel {
color: var(--modal-contrast);
background: var(--modal-accent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-modal__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
padding: 1.4rem 1.4rem 1.15rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-modal__heading {
display: flex;
align-items: flex-start;
gap: 0.9rem;
min-width: 0;
}
.wire-modal__icon {
flex: 0 0 auto;
width: 1.3rem;
height: 1.3rem;
margin-top: 0.15rem;
color: var(--modal-accent);
}
.wire-modal[data-variant="solid"] .wire-modal__icon {
color: currentColor;
}
.wire-modal__heading-copy {
display: grid;
gap: 0.3rem;
min-width: 0;
}
.wire-modal__heading-copy h2,
.wire-modal__heading-copy p {
margin: 0;
}
.wire-modal__heading-copy h2 {
font-size: 1.08rem;
font-weight: 650;
line-height: 1.3;
}
.wire-modal__heading-copy p {
color: var(--wire-color-text-muted);
font-size: 0.8rem;
line-height: 1.55;
}
.wire-modal[data-variant="solid"] .wire-modal__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
.wire-modal__close {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.35rem;
height: 2.35rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: 0.75rem;
cursor: pointer;
}
.wire-modal__close:hover,
.wire-modal__close:focus-visible {
color: var(--modal-accent);
border-color: color-mix(in srgb, var(--modal-accent) 34%, var(--wire-color-border));
outline: none;
}
.wire-modal__body {
flex: 1 1 auto;
min-height: 0;
padding: 1.4rem;
}
.wire-modal[data-scrollable="true"] .wire-modal__body {
overflow: auto;
overscroll-behavior: contain;
}
.wire-modal__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1rem 1.4rem 1.4rem;
border-top: 1px solid var(--wire-color-border);
}
.wire-modal__custom-footer:empty {
display: none;
}
.wire-modal__actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.65rem;
margin-left: auto;
}
.wire-modal__button {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
min-height: 2.55rem;
padding: 0.65rem 1rem;
border-radius: 0.78rem;
font: inherit;
font-size: 0.83rem;
font-weight: 650;
cursor: pointer;
transition: transform 150ms ease, opacity 150ms ease, border-color 150ms ease;
}
.wire-modal__button:active {
transform: scale(0.985);
}
.wire-modal__button--secondary {
color: var(--wire-color-text);
background: transparent;
border: 1px solid var(--wire-color-border);
}
.wire-modal__button--primary {
color: var(--modal-contrast);
background: var(--modal-accent);
border: 1px solid transparent;
}
.wire-modal__button:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.wire-modal__spinner {
width: 0.95rem;
height: 0.95rem;
border: 2px solid color-mix(in srgb, currentColor 35%, transparent);
border-top-color: currentColor;
border-radius: 50%;
animation: wire-modal-spin 750ms linear infinite;
}
@keyframes wire-modal-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 639px) {
.wire-modal__layer {
align-items: flex-end;
padding: 0;
}
.wire-modal__panel,
.wire-modal[data-size="sm"] .wire-modal__panel,
.wire-modal[data-size="lg"] .wire-modal__panel,
.wire-modal[data-size="xl"] .wire-modal__panel {
width: 100%;
max-height: 92vh;
border-radius: 1.25rem 1.25rem 0 0;
}
.wire-modal__footer {
align-items: stretch;
flex-direction: column;
}
.wire-modal__actions {
width: 100%;
}
.wire-modal__button {
flex: 1 1 0;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-modal__button,
.wire-modal__spinner {
animation: none;
transition: none;
}
}
}
}
+645 -63
View File
@@ -4,11 +4,17 @@ component PageHeader {
title = ""
description = ""
icon = ""
id = "page-title"
align = "left"
size = "default"
centered = false
compact = false
size = "default"
maxWidth = "xl"
showBreadcrumbs = false
breadcrumbs = []
breadcrumbParent = ""
breadcrumbParentHref = ""
breadcrumbCurrent = ""
primaryLabel = ""
primaryHref = ""
primaryIcon = ""
@@ -17,89 +23,665 @@ component PageHeader {
secondaryIcon = ""
color = "primary"
variant = "default"
borderBottom = true
class = ""
}
view {
<div
<section
{...attrs}
data-ui-component="PageHeader"
data-size='{size}'
data-align='{centered ? "center" : align}'
data-color='{color}'
class='{class}'
data-variant='{variant}'
data-compact='{compact ? "true" : "false"}'
data-max-width='{maxWidth}'
data-border-bottom='{borderBottom ? "true" : "false"}'
aria-labelledby='{id}'
class='wire-page-header {class}'
>
<Section
spacing='{compact || size === "compact" ? "md" : "lg"}'
variant='{variant}'
color='{color}'
borderBottom="true"
<div
class="wire-page-header__decoration"
aria-hidden="true"
>
<span
class="wire-page-header__glow wire-page-header__glow--one"
>
</span>
<span
class="wire-page-header__glow wire-page-header__glow--two"
>
</span>
<span
class="wire-page-header__grid-pattern"
>
</span>
</div>
<div
class="wire-page-header__inner"
>
{#if showBreadcrumbs && breadcrumbs.length > 0}
<Breadcrumb
label="Breadcrumb"
items='{breadcrumbs}'
active='{breadcrumbs[breadcrumbs.length - 1]?.value || breadcrumbs[breadcrumbs.length - 1]?.label || ""}'
color='{color}'
class="mb-6"
/>
<div
class="wire-page-header__breadcrumb"
>
<Breadcrumb
label="Breadcrumb"
items='{breadcrumbs}'
active='{breadcrumbs[breadcrumbs.length - 1]?.value || breadcrumbs[breadcrumbs.length - 1]?.label || breadcrumbs[breadcrumbs.length - 1]?.title || ""}'
color='{color}'
size='{compact ? "sm" : "default"}'
variant='{variant === "solid" ? "contrast" : "minimal"}'
/>
</div>
{:else if showBreadcrumbs && breadcrumbParent}
<div
class="wire-page-header__breadcrumb"
>
<Breadcrumb
label="Breadcrumb"
items='{[
{
label: breadcrumbParent,
href: breadcrumbParentHref,
value: breadcrumbParent
},
{
label: breadcrumbCurrent || title,
value: breadcrumbCurrent || title,
current: true
}
]}'
active='{breadcrumbCurrent || title}'
color='{color}'
size='{compact ? "sm" : "default"}'
variant='{variant === "solid" ? "contrast" : "minimal"}'
/>
</div>
{:else if showBreadcrumbs && (breadcrumbCurrent || title)}
<div
class="wire-page-header__breadcrumb"
>
<Breadcrumb
label="Breadcrumb"
items='{[
{
label: breadcrumbCurrent || title,
value: breadcrumbCurrent || title,
current: true
}
]}'
active='{breadcrumbCurrent || title}'
color='{color}'
size='{compact ? "sm" : "default"}'
variant='{variant === "solid" ? "contrast" : "minimal"}'
/>
</div>
{/if}
<div
class="flex flex-col gap-6"
class:items-center='align === "center"'
class:text-center='align === "center"'
class:lg:flex-row='align !== "center"'
class:lg:items-end='align !== "center"'
class:lg:justify-between='align !== "center"'
class="wire-page-header__layout"
>
<div class="max-w-4xl">
{#if icon}
<span class="mb-5 inline-flex size-12 items-center justify-center rounded-2xl bg-[var(--wire-color-primary-soft)] text-[var(--wire-color-primary)]">
<span class='{icon + " size-6"}' aria-hidden="true"></span>
</span>
{/if}
<SectionHeader
eyebrow='{eyebrow}'
title='{title}'
description='{description}'
align='{align === "center" ? "center" : "left"}'
size='{compact || size === "compact" ? "sm" : "lg"}'
color='{color}'
headingLevel="1"
maxWidth="4xl"
<div
class="wire-page-header__content"
>
<div
class="wire-page-header__intro"
>
<slot name="meta"></slot>
</SectionHeader>
{#if icon}
<span
class="wire-page-header__icon-wrap"
>
{#if icon === "sparkles"}
<span
class="icon-[lucide--sparkles] wire-page-header__icon"
aria-hidden="true"
>
</span>
{:else if icon === "component"}
<span
class="icon-[lucide--component] wire-page-header__icon"
aria-hidden="true"
>
</span>
{:else}
<span
class='{icon} wire-page-header__icon'
aria-hidden="true"
>
</span>
{/if}
</span>
{/if}
<div
class="wire-page-header__copy"
>
{#if eyebrow}
<div
class="wire-page-header__eyebrow-row"
>
<span
class="wire-page-header__eyebrow-mark"
aria-hidden="true"
>
</span>
<p
class="wire-page-header__eyebrow"
>
{eyebrow}
</p>
</div>
{/if}
{#if title}
<h1
id='{id}'
class="wire-page-header__title"
>
{title}
</h1>
{/if}
{#if description}
<p
class="wire-page-header__description"
>
{description}
</p>
{/if}
<div
class="wire-page-header__meta"
>
<slot
name="meta"
>
</slot>
</div>
</div>
</div>
</div>
<div class="flex flex-wrap items-center gap-3" class:justify-center='align === "center"'>
{#if secondaryLabel}
<Button
label='{secondaryLabel}'
href='{secondaryHref}'
icon='{secondaryIcon}'
variant="outline"
color='{color}'
/>
{/if}
{#if primaryLabel || secondaryLabel}
<div
class="wire-page-header__actions"
>
{#if secondaryLabel}
<Button
label='{secondaryLabel}'
href='{secondaryHref || "#"}'
icon='{secondaryIcon}'
size='{compact ? "sm" : "default"}'
variant="outline"
color='{color}'
class="wire-page-header__action wire-page-header__action--secondary"
/>
{/if}
{#if primaryLabel}
<Button
label='{primaryLabel}'
href='{primaryHref}'
icon='{primaryIcon}'
variant="default"
color='{color}'
/>
{/if}
{#if primaryLabel}
<Button
label='{primaryLabel}'
href='{primaryHref || "#"}'
icon='{primaryIcon}'
size='{compact ? "sm" : "default"}'
variant="default"
color='{color}'
class="wire-page-header__action wire-page-header__action--primary"
/>
{/if}
<slot name="actions"></slot>
</div>
<slot
name="actions"
>
</slot>
</div>
{:else}
<div
class="wire-page-header__actions wire-page-header__actions--slot-only"
>
<slot
name="actions"
>
</slot>
</div>
{/if}
</div>
<slot></slot>
</Section>
</div>
<div
class="wire-page-header__body"
>
<slot></slot>
</div>
</div>
</section>
}
style {
.wire-page-header {
--page-header-accent: var(--wire-color-primary);
--page-header-soft: var(--wire-color-primary-soft);
--page-header-muted: var(--wire-color-primary-muted);
--page-header-accent-text: var(--wire-color-primary-text);
position: relative;
isolation: isolate;
width: 100%;
overflow: hidden;
color: var(--wire-color-text);
background: var(--wire-color-background);
}
.wire-page-header[data-color="secondary"] {
--page-header-accent: var(--wire-color-secondary);
--page-header-soft: var(--wire-color-secondary-soft);
--page-header-muted: var(--wire-color-secondary-muted);
--page-header-accent-text: var(--wire-color-secondary-text);
}
.wire-page-header[data-color="info"] {
--page-header-accent: var(--wire-color-info);
--page-header-soft: var(--wire-color-info-soft);
--page-header-muted: var(--wire-color-info-muted);
--page-header-accent-text: var(--wire-color-info-text);
}
.wire-page-header[data-color="success"] {
--page-header-accent: var(--wire-color-success);
--page-header-soft: var(--wire-color-success-soft);
--page-header-muted: var(--wire-color-success-muted);
--page-header-accent-text: var(--wire-color-success-text);
}
.wire-page-header[data-color="warning"] {
--page-header-accent: var(--wire-color-warning);
--page-header-soft: var(--wire-color-warning-soft);
--page-header-muted: var(--wire-color-warning-muted);
--page-header-accent-text: var(--wire-color-warning-text);
}
.wire-page-header[data-color="danger"] {
--page-header-accent: var(--wire-color-danger);
--page-header-soft: var(--wire-color-danger-soft);
--page-header-muted: var(--wire-color-danger-muted);
--page-header-accent-text: var(--wire-color-danger-text);
}
.wire-page-header[data-variant="soft"] {
background: var(--wire-color-surface);
}
.wire-page-header[data-variant="raised"] {
background: var(--wire-color-surface-raised);
box-shadow: var(--wire-shadow-sm);
}
.wire-page-header[data-variant="tinted"] {
background:
linear-gradient(135deg, var(--page-header-soft), transparent 72%),
var(--wire-color-background);
}
.wire-page-header[data-variant="solid"] {
color: #ffffff;
background: var(--page-header-accent);
}
.wire-page-header[data-variant="minimal"] {
background: transparent;
}
.wire-page-header[data-border-bottom="true"] {
border-bottom: 1px solid var(--wire-color-border);
}
.wire-page-header[data-variant="solid"][data-border-bottom="true"] {
border-bottom-color: rgba(255, 255, 255, 0.18);
}
.wire-page-header__decoration {
position: absolute;
inset: 0;
z-index: -1;
overflow: hidden;
pointer-events: none;
}
.wire-page-header__glow {
position: absolute;
display: block;
border-radius: 999px;
filter: blur(60px);
opacity: 0.17;
}
.wire-page-header__glow--one {
top: -9rem;
right: -5rem;
width: 24rem;
height: 24rem;
background: var(--page-header-accent);
}
.wire-page-header__glow--two {
bottom: -10rem;
left: 18%;
width: 18rem;
height: 18rem;
background: var(--page-header-muted);
opacity: 0.13;
}
.wire-page-header__grid-pattern {
position: absolute;
inset: 0;
opacity: 0.2;
background-image:
linear-gradient(to right, var(--wire-color-border) 1px, transparent 1px),
linear-gradient(to bottom, var(--wire-color-border) 1px, transparent 1px);
background-size: 32px 32px;
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.35), transparent 78%);
}
.wire-page-header[data-variant="minimal"] .wire-page-header__decoration,
.wire-page-header[data-compact="true"] .wire-page-header__grid-pattern {
display: none;
}
.wire-page-header[data-variant="solid"] .wire-page-header__grid-pattern {
opacity: 0.1;
background-image:
linear-gradient(to right, rgba(255, 255, 255, 0.28) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255, 255, 255, 0.28) 1px, transparent 1px);
}
.wire-page-header__inner {
position: relative;
width: 100%;
max-width: 80rem;
margin-inline: auto;
padding: 1.25rem 1rem 2.75rem;
}
.wire-page-header[data-max-width="compact"] .wire-page-header__inner,
.wire-page-header[data-max-width="lg"] .wire-page-header__inner {
max-width: 64rem;
}
.wire-page-header[data-max-width="wide"] .wire-page-header__inner,
.wire-page-header[data-max-width="2xl"] .wire-page-header__inner {
max-width: 90rem;
}
.wire-page-header[data-max-width="full"] .wire-page-header__inner {
max-width: none;
}
.wire-page-header[data-compact="true"] .wire-page-header__inner {
padding-top: 1rem;
padding-bottom: 1.75rem;
}
.wire-page-header[data-size="lg"] .wire-page-header__inner {
padding-bottom: 3.5rem;
}
.wire-page-header__breadcrumb {
display: flex;
align-items: center;
min-width: 0;
margin-bottom: 1.75rem;
padding-bottom: 0.9rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-page-header[data-variant="solid"] .wire-page-header__breadcrumb {
border-bottom-color: rgba(255, 255, 255, 0.18);
}
.wire-page-header[data-compact="true"] .wire-page-header__breadcrumb {
margin-bottom: 1.25rem;
padding-bottom: 0.7rem;
}
.wire-page-header__layout {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1.75rem;
align-items: start;
}
.wire-page-header__content,
.wire-page-header__copy {
min-width: 0;
}
.wire-page-header__intro {
display: flex;
align-items: flex-start;
gap: 1rem;
min-width: 0;
}
.wire-page-header__icon-wrap {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 3.25rem;
height: 3.25rem;
margin-top: 0.15rem;
color: var(--page-header-accent);
background: var(--page-header-soft);
border: 1px solid var(--page-header-muted);
border-radius: 1rem;
box-shadow: var(--wire-shadow-sm);
}
.wire-page-header[data-compact="true"] .wire-page-header__icon-wrap {
width: 2.75rem;
height: 2.75rem;
border-radius: 0.8rem;
}
.wire-page-header[data-variant="solid"] .wire-page-header__icon-wrap {
color: #ffffff;
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.22);
}
.wire-page-header__icon {
width: 1.35rem;
height: 1.35rem;
}
.wire-page-header__eyebrow-row {
display: flex;
align-items: center;
gap: 0.65rem;
margin-bottom: 0.65rem;
}
.wire-page-header__eyebrow-mark {
width: 1.75rem;
height: 2px;
flex: 0 0 auto;
background: var(--page-header-accent);
border-radius: 999px;
}
.wire-page-header__eyebrow {
margin: 0;
color: var(--page-header-accent-text);
font-size: 0.72rem;
font-weight: 800;
line-height: 1.3;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.wire-page-header[data-variant="solid"] .wire-page-header__eyebrow,
.wire-page-header[data-variant="solid"] .wire-page-header__title,
.wire-page-header[data-variant="solid"] .wire-page-header__description {
color: #ffffff;
}
.wire-page-header[data-variant="solid"] .wire-page-header__eyebrow-mark {
background: rgba(255, 255, 255, 0.8);
}
.wire-page-header__title {
max-width: 54rem;
margin: 0;
color: var(--wire-color-text);
font-size: clamp(2rem, 4vw, 3.75rem);
font-weight: 800;
line-height: 1.08;
letter-spacing: -0.035em;
text-wrap: balance;
}
.wire-page-header[data-compact="true"] .wire-page-header__title {
font-size: clamp(1.75rem, 3vw, 2.65rem);
}
.wire-page-header[data-size="lg"] .wire-page-header__title {
font-size: clamp(2.35rem, 5vw, 4.5rem);
}
.wire-page-header__description {
max-width: 48rem;
margin: 0.9rem 0 0;
color: var(--wire-color-text-muted);
font-size: 1rem;
line-height: 1.75;
text-wrap: pretty;
}
.wire-page-header[data-size="lg"] .wire-page-header__description {
font-size: 1.075rem;
}
.wire-page-header[data-variant="solid"] .wire-page-header__description {
opacity: 0.82;
}
.wire-page-header__meta:empty,
.wire-page-header__body:empty,
.wire-page-header__actions--slot-only:empty {
display: none;
}
.wire-page-header__meta {
margin-top: 1.1rem;
}
.wire-page-header__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
min-width: 0;
}
.wire-page-header__body {
margin-top: 1.75rem;
}
.wire-page-header[data-align="center"] .wire-page-header__layout {
justify-items: center;
text-align: center;
}
.wire-page-header[data-align="center"] .wire-page-header__intro {
flex-direction: column;
align-items: center;
}
.wire-page-header[data-align="center"] .wire-page-header__eyebrow-row,
.wire-page-header[data-align="center"] .wire-page-header__actions {
justify-content: center;
}
.wire-page-header[data-align="center"] .wire-page-header__title,
.wire-page-header[data-align="center"] .wire-page-header__description {
margin-inline: auto;
}
.wire-page-header[data-align="right"] .wire-page-header__layout {
justify-items: end;
text-align: right;
}
.wire-page-header[data-align="right"] .wire-page-header__intro {
flex-direction: row-reverse;
}
.wire-page-header[data-align="right"] .wire-page-header__eyebrow-row,
.wire-page-header[data-align="right"] .wire-page-header__actions {
justify-content: flex-end;
}
.wire-page-header[data-align="right"] .wire-page-header__title,
.wire-page-header[data-align="right"] .wire-page-header__description {
margin-left: auto;
}
@media (min-width: 640px) {
.wire-page-header__inner {
padding-inline: 1.5rem;
}
.wire-page-header__intro {
gap: 1.25rem;
}
}
@media (min-width: 1024px) {
.wire-page-header__inner {
padding-inline: 2rem;
}
.wire-page-header__layout {
grid-template-columns: minmax(0, 1fr) auto;
align-items: start;
gap: 2.5rem;
}
.wire-page-header[data-align="center"] .wire-page-header__layout,
.wire-page-header[data-align="right"] .wire-page-header__layout {
grid-template-columns: minmax(0, 1fr);
}
.wire-page-header__actions {
align-self: start;
justify-self: end;
justify-content: flex-end;
padding-top: 0;
padding-bottom: 0;
}
}
@media (max-width: 639px) {
.wire-page-header__intro {
flex-direction: column;
}
.wire-page-header__actions {
display: grid;
grid-template-columns: minmax(0, 1fr);
}
.wire-page-header__action {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-page-header__glow {
filter: blur(52px);
}
}
}
}
+521 -9
View File
@@ -1,21 +1,533 @@
component Popover {
props {
@event toggle = function
@event open = function
@event close = function
@event action = function
open = false
defaultOpen = false
triggerLabel = "Open popover"
triggerIcon = ""
title = ""
description = ""
icon = ""
placement = "bottom-start"
width = "md"
size = "default"
color = "primary"
title = "Popover"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
variant = "raised"
showArrow = true
showClose = false
closeLabel = "Close popover"
closeOnOutside = true
closeOnEscape = true
actionLabel = ""
actionHref = ""
actionIcon = ""
closeOnAction = true
disabled = false
class = ""
}
state visible = defaultOpen
functions {
function isOpen() {
return open || visible
}
function showPopover(sourceEvent) {
if (disabled) {
return
}
visible = true
$emit("open", { sourceEvent: sourceEvent })
$emit("toggle", { open: true, sourceEvent: sourceEvent })
}
function hidePopover(reason, sourceEvent) {
visible = false
$emit("close", { reason: reason, sourceEvent: sourceEvent })
$emit("toggle", { open: false, reason: reason, sourceEvent: sourceEvent })
}
function togglePopover(sourceEvent) {
if (isOpen()) {
hidePopover("toggle", sourceEvent)
} else {
showPopover(sourceEvent)
}
}
function activateAction(sourceEvent) {
$emit("action", {
href: actionHref,
sourceEvent: sourceEvent
})
if (closeOnAction) {
hidePopover("action", sourceEvent)
}
}
function handleKeydown(sourceEvent) {
if (closeOnEscape && sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
hidePopover("escape", sourceEvent)
}
}
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--popover wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
<div
{...attrs}
data-ui-component="Popover"
data-open='{open || visible ? "true" : "false"}'
data-placement='{placement}'
data-width='{width}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-popover {class}'
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-popover__trigger"
disabled='{disabled}'
aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}'
@click='togglePopover(event)'
>
<slot name="trigger"></slot>
{#if triggerIcon}
<span class='{triggerIcon}' aria-hidden="true"></span>
{/if}
{#if triggerLabel}
<span>{triggerLabel}</span>
{/if}
</button>
{#if closeOnOutside}
<button
type="button"
class="wire-popover__dismiss-layer"
data-show='{open || visible}'
aria-label='{closeLabel}'
@click='hidePopover("outside", event)'
></button>
{/if}
<section
class="wire-popover__panel"
data-show='{open || visible}'
role="dialog"
aria-label='{title || triggerLabel}'
>
{#if showArrow}
<span class="wire-popover__arrow" aria-hidden="true"></span>
{/if}
{#if title || description || icon || showClose}
<header class="wire-popover__header">
<div class="wire-popover__heading">
{#if icon}
<span class='wire-popover__icon {icon}' aria-hidden="true"></span>
{/if}
<div class="wire-popover__heading-copy">
<slot name="header"></slot>
{#if title}
<h3>{title}</h3>
{/if}
{#if description}
<p>{description}</p>
{/if}
</div>
</div>
{#if showClose}
<button
type="button"
class="wire-popover__close"
aria-label='{closeLabel}'
@click='hidePopover("close-button", event)'
>
<span class="icon-[lucide--x]" aria-hidden="true"></span>
</button>
{/if}
</header>
{/if}
<div class="wire-popover__body">
<slot></slot>
</div>
{#if actionLabel}
<footer class="wire-popover__footer">
{#if actionHref}
<a
href='{actionHref}'
class="wire-popover__action"
@click='activateAction(event)'
>
<span>{actionLabel}</span>
{#if actionIcon}
<span class='{actionIcon}' aria-hidden="true"></span>
{:else}
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
{/if}
</a>
{:else}
<button
type="button"
class="wire-popover__action"
@click='activateAction(event)'
>
<span>{actionLabel}</span>
{#if actionIcon}
<span class='{actionIcon}' aria-hidden="true"></span>
{:else}
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
{/if}
</button>
{/if}
</footer>
{/if}
<slot name="footer"></slot>
</section>
</div>
}
style {
.wire-popover {
--popover-accent: var(--wire-color-primary);
--popover-soft: var(--wire-color-primary-soft);
--popover-contrast: var(--wire-color-primary-contrast);
position: relative;
display: inline-flex;
}
.wire-popover[data-color="secondary"] {
--popover-accent: var(--wire-color-secondary);
--popover-soft: var(--wire-color-secondary-soft);
--popover-contrast: var(--wire-color-secondary-contrast);
}
.wire-popover[data-color="info"] {
--popover-accent: var(--wire-color-info);
--popover-soft: var(--wire-color-info-soft);
--popover-contrast: var(--wire-color-info-contrast);
}
.wire-popover[data-color="success"] {
--popover-accent: var(--wire-color-success);
--popover-soft: var(--wire-color-success-soft);
--popover-contrast: var(--wire-color-success-contrast);
}
.wire-popover[data-color="warning"] {
--popover-accent: var(--wire-color-warning);
--popover-soft: var(--wire-color-warning-soft);
--popover-contrast: var(--wire-color-warning-text);
}
.wire-popover[data-color="danger"] {
--popover-accent: var(--wire-color-danger);
--popover-soft: var(--wire-color-danger-soft);
--popover-contrast: var(--wire-color-on-danger);
}
.wire-popover__trigger {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-height: 2.55rem;
padding: 0.65rem 0.9rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 0.78rem;
font: inherit;
font-size: 0.84rem;
font-weight: 600;
cursor: pointer;
transition: color 150ms ease, border-color 150ms ease, background-color 150ms ease;
}
.wire-popover__trigger:hover,
.wire-popover__trigger:focus-visible,
.wire-popover[data-open="true"] .wire-popover__trigger {
color: var(--popover-accent);
background: var(--popover-soft);
border-color: color-mix(in srgb, var(--popover-accent) 38%, var(--wire-color-border));
outline: none;
}
.wire-popover__trigger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.wire-popover[data-size="sm"] .wire-popover__trigger {
min-height: 2.2rem;
padding: 0.5rem 0.72rem;
font-size: 0.78rem;
}
.wire-popover[data-size="lg"] .wire-popover__trigger {
min-height: 2.9rem;
padding: 0.78rem 1.05rem;
font-size: 0.9rem;
}
.wire-popover__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1120;
appearance: none;
padding: 0;
background: transparent;
border: 0;
}
.wire-popover__panel {
position: absolute;
z-index: 1121;
top: calc(100% + 0.72rem);
left: 0;
width: 18rem;
max-width: calc(100vw - 1.5rem);
color: var(--wire-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--popover-accent) 5%, transparent), transparent 62%),
color-mix(in srgb, var(--wire-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 64px color-mix(in srgb, black 22%, transparent);
backdrop-filter: blur(18px);
}
.wire-popover[data-width="sm"] .wire-popover__panel {
width: 14rem;
}
.wire-popover[data-width="lg"] .wire-popover__panel {
width: 24rem;
}
.wire-popover[data-width="xl"] .wire-popover__panel {
width: 32rem;
}
.wire-popover[data-width="trigger"] .wire-popover__panel {
width: 100%;
min-width: 100%;
}
.wire-popover[data-placement="bottom-end"] .wire-popover__panel {
right: 0;
left: auto;
}
.wire-popover[data-placement="top-start"] .wire-popover__panel {
top: auto;
bottom: calc(100% + 0.72rem);
}
.wire-popover[data-placement="top-end"] .wire-popover__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.72rem);
left: auto;
}
.wire-popover[data-placement="left"] .wire-popover__panel {
top: 50%;
right: calc(100% + 0.72rem);
left: auto;
transform: translateY(-50%);
}
.wire-popover[data-placement="right"] .wire-popover__panel {
top: 50%;
left: calc(100% + 0.72rem);
transform: translateY(-50%);
}
.wire-popover[data-variant="soft"] .wire-popover__panel {
background:
linear-gradient(145deg, var(--popover-soft), transparent 70%),
var(--wire-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-popover[data-variant="outline"] .wire-popover__panel {
background: var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-popover[data-variant="solid"] .wire-popover__panel {
color: var(--popover-contrast);
background: var(--popover-accent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-popover__arrow {
position: absolute;
top: -0.35rem;
left: 1.25rem;
width: 0.7rem;
height: 0.7rem;
background: inherit;
border-top: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
border-left: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
transform: rotate(45deg);
}
.wire-popover[data-placement="bottom-end"] .wire-popover__arrow {
right: 1.25rem;
left: auto;
}
.wire-popover[data-placement="top-start"] .wire-popover__arrow,
.wire-popover[data-placement="top-end"] .wire-popover__arrow {
top: auto;
bottom: -0.35rem;
transform: rotate(225deg);
}
.wire-popover[data-placement="left"] .wire-popover__arrow {
top: 50%;
right: -0.35rem;
left: auto;
transform: translateY(-50%) rotate(135deg);
}
.wire-popover[data-placement="right"] .wire-popover__arrow {
top: 50%;
left: -0.35rem;
transform: translateY(-50%) rotate(-45deg);
}
.wire-popover__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.8rem;
padding: 1rem 1rem 0.8rem;
border-bottom: 1px solid var(--wire-color-border);
}
.wire-popover__heading {
display: flex;
align-items: flex-start;
gap: 0.7rem;
min-width: 0;
}
.wire-popover__icon {
flex: 0 0 auto;
width: 1.05rem;
height: 1.05rem;
margin-top: 0.15rem;
color: var(--popover-accent);
}
.wire-popover[data-variant="solid"] .wire-popover__icon {
color: currentColor;
}
.wire-popover__heading-copy {
display: grid;
gap: 0.2rem;
min-width: 0;
}
.wire-popover__heading-copy h3,
.wire-popover__heading-copy p {
margin: 0;
}
.wire-popover__heading-copy h3 {
font-size: 0.9rem;
font-weight: 650;
}
.wire-popover__heading-copy p {
color: var(--wire-color-text-muted);
font-size: 0.74rem;
line-height: 1.5;
}
.wire-popover[data-variant="solid"] .wire-popover__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
.wire-popover__close {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2rem;
height: 2rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: 0.65rem;
cursor: pointer;
}
.wire-popover__body {
padding: 1rem;
color: var(--wire-color-text-muted);
font-size: 0.8rem;
line-height: 1.6;
}
.wire-popover__footer {
padding: 0 1rem 1rem;
}
.wire-popover__action {
appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.45rem;
min-height: 2.35rem;
padding: 0.58rem 0.8rem;
color: var(--popover-contrast);
background: var(--popover-accent);
border: 0;
border-radius: 0.7rem;
font: inherit;
font-size: 0.78rem;
font-weight: 650;
text-decoration: none;
cursor: pointer;
}
@media (max-width: 639px) {
.wire-popover__panel {
position: fixed;
right: 0.75rem;
bottom: 0.75rem;
left: 0.75rem;
top: auto;
width: auto;
max-width: none;
transform: none;
}
.wire-popover__arrow {
display: none;
}
}
}
}
+782 -63
View File
@@ -1,6 +1,7 @@
component SplitHero {
props {
eyebrow = ""
eyebrowIcon = "icon-[lucide--sparkles]"
title = "A better digital experience"
highlight = ""
description = ""
@@ -13,86 +14,804 @@ component SplitHero {
visualPosition = "right"
reverse = false
ratio = "balanced"
align = "left"
size = "default"
color = "primary"
variant = "default"
maxWidth = "xl"
fullBleed = true
backgroundImage = ""
visualImage = ""
visualAlt = ""
visualIcon = ""
visualEyebrow = ""
visualTitle = ""
visualDescription = ""
visualItems = []
visualStyle = "panel"
trustItems = []
class = ""
}
view {
<Section
spacing='{size === "compact" ? "md" : "xl"}'
variant='{variant === "soft" ? "soft" : "default"}'
color='{color}'
class='overflow-hidden {class}'
<section
{...attrs}
data-ui-component="SplitHero"
data-position='{reverse || visualPosition === "left" ? "left" : "right"}'
data-ratio='{ratio}'
data-align='{align}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-max-width='{maxWidth}'
data-full-bleed='{fullBleed ? "true" : "false"}'
data-visual-style='{visualStyle}'
class='wire-split-hero {class}'
>
<div
data-ui-component="SplitHero"
class="grid items-center gap-10 lg:grid-cols-2 lg:gap-16"
class:lg:grid-cols-[1.15fr_0.85fr]='ratio === "content"'
class:lg:grid-cols-[0.85fr_1.15fr]='ratio === "visual"'
>
<div
class="max-w-3xl"
class:lg:order-2='reverse || visualPosition === "left"'
>
{#if eyebrow}
<div class="mb-6 inline-flex items-center gap-2 rounded-full border border-[var(--wire-color-primary-muted)] bg-[var(--wire-color-primary-soft)] px-4 py-2 text-sm font-semibold text-[var(--wire-color-primary)]">
<span class="icon-[lucide--sparkles] size-4" aria-hidden="true"></span>
<span>{eyebrow}</span>
</div>
{/if}
{#if backgroundImage}
<img
src='{backgroundImage}'
alt=""
aria-hidden="true"
class="wire-split-hero__background-image"
/>
{/if}
<h1 class="text-4xl font-bold leading-[1.08] tracking-[-0.035em] text-[var(--wire-color-text)] sm:text-5xl lg:text-6xl">
<span>{title}</span>
{#if highlight}
<span class="block text-[var(--wire-color-primary)]">{highlight}</span>
<div class="wire-split-hero__glow wire-split-hero__glow--one" aria-hidden="true"></div>
<div class="wire-split-hero__glow wire-split-hero__glow--two" aria-hidden="true"></div>
<div class="wire-split-hero__inner">
<div class="wire-split-hero__layout">
<div class="wire-split-hero__content">
{#if eyebrow}
<div class="wire-split-hero__eyebrow">
{#if eyebrowIcon}
<span class='wire-split-hero__eyebrow-icon {eyebrowIcon}' aria-hidden="true"></span>
{/if}
<span>{eyebrow}</span>
</div>
{/if}
</h1>
{#if description}
<p class="mt-6 text-base leading-8 text-[var(--wire-color-text-muted)] sm:text-lg">{description}</p>
{/if}
<h1 class="wire-split-hero__title">
<span>{title}</span>
{#if highlight}
<span class="wire-split-hero__highlight">{highlight}</span>
{/if}
</h1>
<HeroActions
actions='{[
{ label: primaryLabel, href: primaryHref, icon: primaryIcon, variant: "default", color: color },
{ label: secondaryLabel, href: secondaryHref, icon: secondaryIcon, variant: "outline", color: color }
]}'
align="left"
stackOnMobile="true"
fullWidthMobile="true"
size="lg"
color='{color}'
class="mt-8"
>
<slot name="actions"></slot>
</HeroActions>
{#if description}
<p class="wire-split-hero__description">{description}</p>
{/if}
{#if trustItems.length > 0}
<div class="mt-8 flex flex-wrap gap-x-6 gap-y-3 text-sm text-[var(--wire-color-text-muted)]">
{#each trustItems as item}
<span class="inline-flex items-center gap-2">
<span class='{(item.icon || "icon-[lucide--check-circle-2]") + " size-4 text-[var(--wire-color-primary)]"}' aria-hidden="true"></span>
<span>{item.label || item.title || item}</span>
</span>
{/each}
</div>
{/if}
<slot></slot>
<slot name="trust"></slot>
<slot></slot>
</div>
{#if primaryLabel || secondaryLabel}
<div class="wire-split-hero__actions">
{#if primaryLabel}
<a href='{primaryHref || "#"}' class="wire-split-hero__action wire-split-hero__action--primary">
{#if primaryIcon}
<span class='wire-split-hero__action-icon {primaryIcon}' aria-hidden="true"></span>
{/if}
<span>{primaryLabel}</span>
</a>
{/if}
<div
class="relative min-w-0"
class:lg:order-1='reverse || visualPosition === "left"'
>
<div class="absolute -inset-8 -z-10 rounded-[2.5rem] bg-[var(--wire-color-primary-soft)] blur-2xl" aria-hidden="true"></div>
<slot name="visual"></slot>
{#if secondaryLabel}
<a href='{secondaryHref || "#"}' class="wire-split-hero__action wire-split-hero__action--secondary">
{#if secondaryIcon}
<span class='wire-split-hero__action-icon {secondaryIcon}' aria-hidden="true"></span>
{/if}
<span>{secondaryLabel}</span>
</a>
{/if}
<slot name="actions"></slot>
</div>
{:else}
<div class="wire-split-hero__actions wire-split-hero__actions--slot">
<slot name="actions"></slot>
</div>
{/if}
{#if trustItems.length > 0}
<div class="wire-split-hero__trust">
{#each trustItems as item}
<span class="wire-split-hero__trust-item">
<span class='{item.icon || "icon-[lucide--check-circle-2]"}' aria-hidden="true"></span>
<span>{item.label || item.title || item}</span>
</span>
{/each}
</div>
{/if}
<slot name="trust"></slot>
</div>
<div class="wire-split-hero__visual-column">
<slot name="visual"></slot>
{#if visualImage || visualIcon || visualEyebrow || visualTitle || visualDescription || visualItems.length > 0}
<div class="wire-split-hero__visual">
{#if visualImage}
<div class="wire-split-hero__visual-media">
<img src='{visualImage}' alt='{visualAlt}' class="wire-split-hero__visual-image" />
</div>
{/if}
{#if visualIcon || visualEyebrow || visualTitle || visualDescription}
<div class="wire-split-hero__visual-header">
{#if visualIcon}
<div class="wire-split-hero__visual-icon" aria-hidden="true">
<span class='{visualIcon}'></span>
</div>
{/if}
<div class="wire-split-hero__visual-copy">
{#if visualEyebrow}
<p class="wire-split-hero__visual-eyebrow">{visualEyebrow}</p>
{/if}
{#if visualTitle}
<h2 class="wire-split-hero__visual-title">{visualTitle}</h2>
{/if}
{#if visualDescription}
<p class="wire-split-hero__visual-description">{visualDescription}</p>
{/if}
</div>
</div>
{/if}
{#if visualItems.length > 0}
<div class="wire-split-hero__visual-list">
{#each visualItems as item}
<div class="wire-split-hero__visual-item">
{#if item.icon}
<div class="wire-split-hero__visual-item-icon" aria-hidden="true">
<span class='{item.icon}'></span>
</div>
{/if}
<div class="wire-split-hero__visual-item-copy">
<div class="wire-split-hero__visual-item-heading">
<strong>{item.label || item.title}</strong>
{#if item.value}
<span>{item.value}</span>
{/if}
</div>
{#if item.description}
<p>{item.description}</p>
{/if}
</div>
</div>
{/each}
</div>
{/if}
</div>
{/if}
</div>
</div>
</div>
</Section>
</section>
}
style {
.wire-split-hero {
--split-accent: var(--wire-color-primary);
--split-accent-hover: var(--wire-color-primary-hover);
--split-accent-soft: var(--wire-color-primary-soft);
--split-accent-muted: var(--wire-color-primary-muted);
--split-contrast: var(--wire-color-primary-contrast);
--split-border: color-mix(in srgb, var(--split-accent) 14%, var(--wire-color-border));
position: relative;
isolation: isolate;
width: 100%;
overflow: hidden;
color: var(--wire-color-text);
background:
radial-gradient(circle at 88% 18%, color-mix(in srgb, var(--split-accent) 11%, transparent), transparent 28%),
var(--wire-color-background);
}
.wire-split-hero[data-color="secondary"] {
--split-accent: var(--wire-color-secondary);
--split-accent-hover: var(--wire-color-secondary-hover);
--split-accent-soft: var(--wire-color-secondary-soft);
--split-accent-muted: var(--wire-color-secondary-muted);
--split-contrast: var(--wire-color-secondary-contrast);
}
.wire-split-hero[data-color="info"] {
--split-accent: var(--wire-color-info);
--split-accent-hover: var(--wire-color-info-hover);
--split-accent-soft: var(--wire-color-info-soft);
--split-accent-muted: var(--wire-color-info-muted);
--split-contrast: var(--wire-color-info-contrast);
}
.wire-split-hero[data-color="success"] {
--split-accent: var(--wire-color-success);
--split-accent-hover: var(--wire-color-success-hover);
--split-accent-soft: var(--wire-color-success-soft);
--split-accent-muted: var(--wire-color-success-muted);
--split-contrast: var(--wire-color-success-contrast);
}
.wire-split-hero[data-color="warning"] {
--split-accent: var(--wire-color-warning);
--split-accent-hover: var(--wire-color-warning-hover);
--split-accent-soft: var(--wire-color-warning-soft);
--split-accent-muted: var(--wire-color-warning-muted);
--split-contrast: var(--wire-color-warning-contrast);
}
.wire-split-hero[data-color="danger"] {
--split-accent: var(--wire-color-danger);
--split-accent-hover: var(--wire-color-danger-hover);
--split-accent-soft: var(--wire-color-danger-soft);
--split-accent-muted: var(--wire-color-danger-muted);
--split-contrast: var(--wire-color-danger-contrast);
}
.wire-split-hero[data-variant="soft"] {
background:
radial-gradient(circle at 84% 18%, color-mix(in srgb, var(--split-accent) 16%, transparent), transparent 30%),
linear-gradient(135deg, var(--split-accent-soft), transparent 64%),
var(--wire-color-background);
}
.wire-split-hero[data-variant="raised"] {
background: var(--wire-color-surface-raised);
border-block: 1px solid var(--split-border);
box-shadow: 0 22px 70px color-mix(in srgb, black 14%, transparent);
}
.wire-split-hero[data-variant="outline"] {
background: transparent;
border-block: 1px solid var(--split-border);
}
.wire-split-hero[data-variant="gradient"] {
background:
radial-gradient(circle at 82% 16%, color-mix(in srgb, var(--split-accent) 28%, transparent), transparent 32%),
linear-gradient(135deg, color-mix(in srgb, var(--split-accent) 13%, var(--wire-color-background)), var(--wire-color-background) 58%);
}
.wire-split-hero[data-variant="solid"] {
color: var(--split-contrast);
background:
radial-gradient(circle at 82% 16%, color-mix(in srgb, white 14%, transparent), transparent 32%),
var(--split-accent);
}
.wire-split-hero[data-full-bleed="false"] {
width: min(calc(100% - 2rem), 90rem);
margin-inline: auto;
border: 1px solid var(--split-border);
border-radius: 1.75rem;
}
.wire-split-hero__background-image {
position: absolute;
inset: 0;
z-index: -3;
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.12;
}
.wire-split-hero__glow {
position: absolute;
z-index: -2;
width: 24rem;
height: 24rem;
pointer-events: none;
background: var(--split-accent);
border-radius: 999px;
filter: blur(110px);
opacity: 0.08;
}
.wire-split-hero__glow--one {
top: -11rem;
right: -6rem;
}
.wire-split-hero__glow--two {
bottom: -14rem;
left: -7rem;
opacity: 0.05;
}
.wire-split-hero__inner {
width: min(calc(100% - 2rem), 80rem);
margin-inline: auto;
padding-block: 5rem;
}
.wire-split-hero[data-max-width="compact"] .wire-split-hero__inner {
width: min(calc(100% - 2rem), 64rem);
}
.wire-split-hero[data-max-width="lg"] .wire-split-hero__inner {
width: min(calc(100% - 2rem), 72rem);
}
.wire-split-hero[data-max-width="wide"] .wire-split-hero__inner,
.wire-split-hero[data-max-width="2xl"] .wire-split-hero__inner {
width: min(calc(100% - 2rem), 90rem);
}
.wire-split-hero[data-max-width="full"] .wire-split-hero__inner {
width: 100%;
max-width: none;
padding-inline: 2rem;
}
.wire-split-hero[data-size="compact"] .wire-split-hero__inner,
.wire-split-hero[data-size="sm"] .wire-split-hero__inner {
padding-block: 3rem;
}
.wire-split-hero[data-size="lg"] .wire-split-hero__inner {
padding-block: 6rem;
}
.wire-split-hero__layout {
display: grid;
grid-template-columns: minmax(0, 1fr);
align-items: center;
gap: 3rem;
}
.wire-split-hero__content,
.wire-split-hero__visual-column {
min-width: 0;
}
.wire-split-hero__content {
max-width: 45rem;
}
.wire-split-hero[data-align="center"] .wire-split-hero__content {
margin-inline: auto;
text-align: center;
}
.wire-split-hero[data-align="right"] .wire-split-hero__content {
margin-left: auto;
text-align: right;
}
.wire-split-hero__eyebrow {
display: inline-flex;
align-items: center;
gap: 0.5rem;
min-height: 2.25rem;
padding: 0.45rem 0.8rem;
color: var(--split-accent);
background: var(--split-accent-soft);
border: 1px solid color-mix(in srgb, var(--split-accent) 22%, transparent);
border-radius: 999px;
font-size: 0.76rem;
font-weight: 600;
line-height: 1;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__eyebrow {
color: currentColor;
background: color-mix(in srgb, white 14%, transparent);
border-color: color-mix(in srgb, white 22%, transparent);
}
.wire-split-hero__eyebrow-icon {
width: 0.95rem;
height: 0.95rem;
}
.wire-split-hero__title {
max-width: 15ch;
margin: 1.45rem 0 0;
color: var(--wire-color-text);
font-size: clamp(2.35rem, 6vw, 4.8rem);
font-weight: 650;
line-height: 1.02;
letter-spacing: -0.05em;
}
.wire-split-hero[data-size="compact"] .wire-split-hero__title,
.wire-split-hero[data-size="sm"] .wire-split-hero__title {
font-size: clamp(2rem, 5vw, 3.4rem);
}
.wire-split-hero[data-size="lg"] .wire-split-hero__title {
font-size: clamp(2.75rem, 7vw, 5.5rem);
}
.wire-split-hero[data-align="center"] .wire-split-hero__title,
.wire-split-hero[data-align="right"] .wire-split-hero__title {
margin-inline: auto;
}
.wire-split-hero[data-align="right"] .wire-split-hero__title {
margin-right: 0;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__title {
color: currentColor;
}
.wire-split-hero__highlight {
display: block;
color: var(--split-accent);
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__highlight {
color: currentColor;
opacity: 0.82;
}
.wire-split-hero__description {
max-width: 42rem;
margin: 1.4rem 0 0;
color: var(--wire-color-text-muted);
font-size: clamp(0.96rem, 1.8vw, 1.1rem);
line-height: 1.8;
}
.wire-split-hero[data-align="center"] .wire-split-hero__description,
.wire-split-hero[data-align="right"] .wire-split-hero__description {
margin-inline: auto;
}
.wire-split-hero[data-align="right"] .wire-split-hero__description {
margin-right: 0;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__description {
color: color-mix(in srgb, currentColor 82%, transparent);
}
.wire-split-hero__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
margin-top: 2rem;
}
.wire-split-hero__actions--slot:empty {
display: none;
}
.wire-split-hero[data-align="center"] .wire-split-hero__actions {
justify-content: center;
}
.wire-split-hero[data-align="right"] .wire-split-hero__actions {
justify-content: flex-end;
}
.wire-split-hero__action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-height: 2.85rem;
padding: 0.78rem 1.1rem;
border-radius: 0.75rem;
font-size: 0.84rem;
font-weight: 700;
line-height: 1;
text-decoration: none;
transition:
transform 160ms ease,
background-color 160ms ease,
border-color 160ms ease,
color 160ms ease,
box-shadow 160ms ease;
}
.wire-split-hero__action:hover {
transform: translateY(-2px);
}
.wire-split-hero__action--primary {
color: var(--split-contrast);
background: var(--split-accent);
border: 1px solid transparent;
box-shadow: 0 10px 26px color-mix(in srgb, var(--split-accent) 22%, transparent);
}
.wire-split-hero__action--primary:hover {
background: var(--split-accent-hover);
}
.wire-split-hero__action--secondary {
color: var(--split-accent);
background: transparent;
border: 1px solid color-mix(in srgb, var(--split-accent) 42%, var(--wire-color-border));
}
.wire-split-hero__action--secondary:hover {
background: var(--split-accent-soft);
border-color: var(--split-accent);
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__action--primary {
color: var(--split-accent);
background: var(--split-contrast);
box-shadow: none;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__action--secondary {
color: currentColor;
border-color: color-mix(in srgb, white 46%, transparent);
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__action--secondary:hover {
background: color-mix(in srgb, white 12%, transparent);
border-color: color-mix(in srgb, white 68%, transparent);
}
.wire-split-hero__action-icon {
width: 1rem;
height: 1rem;
}
.wire-split-hero__action:focus-visible {
outline: 2px solid var(--wire-color-focus);
outline-offset: 3px;
}
.wire-split-hero__trust {
display: flex;
flex-wrap: wrap;
gap: 0.75rem 1.2rem;
margin-top: 1.7rem;
color: var(--wire-color-text-muted);
font-size: 0.78rem;
}
.wire-split-hero[data-align="center"] .wire-split-hero__trust {
justify-content: center;
}
.wire-split-hero[data-align="right"] .wire-split-hero__trust {
justify-content: flex-end;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__trust {
color: color-mix(in srgb, currentColor 80%, transparent);
}
.wire-split-hero__trust-item {
display: inline-flex;
align-items: center;
gap: 0.42rem;
}
.wire-split-hero__trust-item > span:first-child {
width: 0.95rem;
height: 0.95rem;
color: var(--split-accent);
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__trust-item > span:first-child {
color: currentColor;
}
.wire-split-hero__visual-column {
position: relative;
}
.wire-split-hero__visual-column::before {
content: "";
position: absolute;
inset: 8% -5% -4% 8%;
z-index: -1;
background: var(--split-accent-soft);
border-radius: 2rem;
filter: blur(2px);
transform: rotate(2deg);
}
.wire-split-hero__visual {
overflow: hidden;
background:
linear-gradient(145deg, color-mix(in srgb, var(--split-accent) 7%, transparent), transparent 58%),
var(--wire-color-surface-raised);
border: 1px solid var(--split-border);
border-radius: 1.5rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 30px 76px color-mix(in srgb, black 18%, transparent);
}
.wire-split-hero[data-visual-style="plain"] .wire-split-hero__visual {
overflow: visible;
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-split-hero[data-visual-style="soft"] .wire-split-hero__visual {
background:
linear-gradient(145deg, var(--split-accent-soft), transparent 72%),
var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-split-hero[data-variant="solid"] .wire-split-hero__visual {
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border-color: color-mix(in srgb, white 24%, transparent);
}
.wire-split-hero__visual-media {
position: relative;
aspect-ratio: 16 / 10;
overflow: hidden;
background: var(--wire-color-surface-soft);
}
.wire-split-hero__visual-image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.wire-split-hero__visual-header {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 1rem;
padding: 1.5rem;
}
.wire-split-hero__visual-icon,
.wire-split-hero__visual-item-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.8rem;
height: 2.8rem;
color: var(--split-accent);
background: var(--split-accent-soft);
border: 1px solid color-mix(in srgb, var(--split-accent) 20%, transparent);
border-radius: 0.9rem;
font-size: 1.2rem;
}
.wire-split-hero__visual-copy,
.wire-split-hero__visual-item-copy {
min-width: 0;
}
.wire-split-hero__visual-eyebrow {
margin: 0 0 0.35rem;
color: var(--split-accent);
font-size: 0.69rem;
font-weight: 700;
letter-spacing: 0.11em;
text-transform: uppercase;
}
.wire-split-hero__visual-title {
margin: 0;
color: var(--wire-color-text);
font-size: 1.15rem;
font-weight: 650;
line-height: 1.35;
}
.wire-split-hero__visual-description {
margin: 0.5rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.82rem;
line-height: 1.65;
}
.wire-split-hero__visual-list {
display: grid;
gap: 0;
border-top: 1px solid var(--wire-color-border);
}
.wire-split-hero__visual-item {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 0.85rem;
padding: 1.1rem 1.5rem;
}
.wire-split-hero__visual-item + .wire-split-hero__visual-item {
border-top: 1px solid var(--wire-color-border);
}
.wire-split-hero__visual-item-icon {
width: 2.25rem;
height: 2.25rem;
border-radius: 0.72rem;
font-size: 1rem;
}
.wire-split-hero__visual-item-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
color: var(--wire-color-text);
font-size: 0.82rem;
}
.wire-split-hero__visual-item-heading > span {
color: var(--split-accent);
font-weight: 700;
}
.wire-split-hero__visual-item p {
margin: 0.35rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.75rem;
line-height: 1.55;
}
@media (min-width: 960px) {
.wire-split-hero__layout {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 4.5rem;
}
.wire-split-hero[data-ratio="content"] .wire-split-hero__layout {
grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
}
.wire-split-hero[data-ratio="visual"] .wire-split-hero__layout {
grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
}
.wire-split-hero[data-position="left"] .wire-split-hero__content {
order: 2;
}
.wire-split-hero[data-position="left"] .wire-split-hero__visual-column {
order: 1;
}
}
@media (max-width: 639px) {
.wire-split-hero__inner {
padding-block: 3.25rem;
}
.wire-split-hero__actions {
align-items: stretch;
}
.wire-split-hero__action {
width: 100%;
}
.wire-split-hero__visual-header,
.wire-split-hero__visual-item {
padding-inline: 1.15rem;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-split-hero__action {
transition: none;
}
.wire-split-hero__action:hover {
transform: none;
}
}
}
}
+465 -21
View File
@@ -13,28 +13,472 @@ component StatsBar {
}
view {
<Section
spacing='{compact ? "sm" : "md"}'
variant='{variant}'
color='{color}'
maxWidth='{maxWidth}'
borderTop="true"
borderBottom="true"
class='{class}'
<section
{...attrs}
data-ui-component="StatsBar"
data-columns='{columns}'
data-compact='{compact ? "true" : "false"}'
data-dividers='{dividers ? "true" : "false"}'
data-icons='{icons ? "true" : "false"}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-max-width='{maxWidth}'
class='wire-stats-bar {class}'
aria-label="Key statistics"
>
<div data-ui-component="StatsBar">
<MetricGrid
items='{items.map((item) => ({ ...item, icon: icons ? item.icon : "", variant: "minimal", size: compact ? "sm" : size }))}'
columns='{columns}'
tabletColumns="2"
mobileColumns="1"
gap='{compact ? "sm" : "md"}'
dividers='{dividers}'
size='{compact ? "sm" : size}'
color='{color}'
variant="minimal"
/>
<div class="wire-stats-bar__inner">
<div class="wire-stats-bar__surface">
{#each items as item, itemIndex}
<article
class="wire-stats-bar__item"
data-color='{item.color || color}'
data-index='{itemIndex}'
>
<div class="wire-stats-bar__content">
{#if item.label}
<p class="wire-stats-bar__label">{item.label}</p>
{/if}
<div class="wire-stats-bar__value-row">
<strong class="wire-stats-bar__value">{item.value}</strong>
{#if item.suffix}
<span class="wire-stats-bar__suffix">{item.suffix}</span>
{/if}
</div>
{#if item.description}
<p class="wire-stats-bar__description">{item.description}</p>
{/if}
{#if item.trend || item.trendLabel}
<div
class="wire-stats-bar__trend"
data-direction='{item.trendDirection || "neutral"}'
>
{#if item.trendDirection === "up"}
<span
class="icon-[lucide--trending-up] wire-stats-bar__trend-icon"
aria-hidden="true"
></span>
{:else if item.trendDirection === "down"}
<span
class="icon-[lucide--trending-down] wire-stats-bar__trend-icon"
aria-hidden="true"
></span>
{:else}
<span
class="icon-[lucide--minus] wire-stats-bar__trend-icon"
aria-hidden="true"
></span>
{/if}
{#if item.trend}
<span class="wire-stats-bar__trend-value">{item.trend}</span>
{/if}
{#if item.trendLabel}
<span class="wire-stats-bar__trend-label">{item.trendLabel}</span>
{/if}
</div>
{/if}
</div>
{#if icons && item.icon}
<div class="wire-stats-bar__icon" aria-hidden="true">
<span class='{item.icon}'></span>
</div>
{/if}
</article>
{/each}
</div>
</div>
</Section>
</section>
}
style {
.wire-stats-bar {
--stats-accent: var(--wire-color-primary);
--stats-contrast: var(--wire-color-primary-contrast);
--stats-soft: color-mix(in srgb, var(--stats-accent) 12%, transparent);
--stats-border: color-mix(in srgb, var(--stats-accent) 22%, var(--wire-color-border));
position: relative;
width: 100%;
color: var(--wire-color-text);
}
.wire-stats-bar[data-color="secondary"] {
--stats-accent: var(--wire-color-secondary);
--stats-contrast: var(--wire-color-secondary-contrast);
}
.wire-stats-bar[data-color="info"] {
--stats-accent: var(--wire-color-info);
--stats-contrast: var(--wire-color-info-contrast);
}
.wire-stats-bar[data-color="success"] {
--stats-accent: var(--wire-color-success);
--stats-contrast: var(--wire-color-success-contrast);
}
.wire-stats-bar[data-color="warning"] {
--stats-accent: var(--wire-color-warning);
--stats-contrast: var(--wire-color-warning-contrast);
}
.wire-stats-bar[data-color="danger"] {
--stats-accent: var(--wire-color-danger);
--stats-contrast: var(--wire-color-danger-contrast);
}
.wire-stats-bar__inner {
width: min(calc(100% - 2rem), 80rem);
margin-inline: auto;
}
.wire-stats-bar[data-max-width="compact"] .wire-stats-bar__inner {
width: min(calc(100% - 2rem), 64rem);
}
.wire-stats-bar[data-max-width="lg"] .wire-stats-bar__inner {
width: min(calc(100% - 2rem), 72rem);
}
.wire-stats-bar[data-max-width="wide"] .wire-stats-bar__inner,
.wire-stats-bar[data-max-width="2xl"] .wire-stats-bar__inner {
width: min(calc(100% - 2rem), 90rem);
}
.wire-stats-bar[data-max-width="full"] .wire-stats-bar__inner {
width: 100%;
max-width: none;
}
.wire-stats-bar__surface {
display: grid;
grid-template-columns: minmax(0, 1fr);
overflow: hidden;
background:
linear-gradient(
135deg,
color-mix(in srgb, var(--stats-accent) 7%, transparent),
transparent 42%
),
var(--wire-color-surface-raised);
border: 1px solid var(--stats-border);
border-radius: 1.25rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-stats-bar[data-variant="default"] .wire-stats-bar__surface {
background: var(--wire-color-surface);
box-shadow: none;
}
.wire-stats-bar[data-variant="soft"] .wire-stats-bar__surface {
background:
linear-gradient(135deg, var(--stats-soft), transparent 58%),
var(--wire-color-surface-raised);
box-shadow: none;
}
.wire-stats-bar[data-variant="outline"] .wire-stats-bar__surface {
background: transparent;
box-shadow: none;
}
.wire-stats-bar[data-variant="minimal"] .wire-stats-bar__surface {
background: transparent;
border-color: transparent;
border-radius: 0;
box-shadow: none;
}
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__surface {
color: var(--stats-contrast);
background:
linear-gradient(135deg, color-mix(in srgb, white 8%, transparent), transparent 52%),
var(--stats-accent);
border-color: color-mix(in srgb, white 24%, transparent);
box-shadow: 0 20px 56px color-mix(in srgb, var(--stats-accent) 28%, transparent);
}
.wire-stats-bar__item {
--item-accent: var(--stats-accent);
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: start;
gap: 1rem;
min-width: 0;
padding: 1.5rem;
transition:
background-color 180ms ease,
transform 180ms ease;
}
.wire-stats-bar[data-compact="true"] .wire-stats-bar__item {
padding: 1.15rem 1.25rem;
}
.wire-stats-bar[data-size="sm"] .wire-stats-bar__item {
padding: 1rem 1.1rem;
}
.wire-stats-bar[data-size="lg"] .wire-stats-bar__item {
padding: 1.8rem;
}
.wire-stats-bar__item[data-color="secondary"] {
--item-accent: var(--wire-color-secondary);
}
.wire-stats-bar__item[data-color="info"] {
--item-accent: var(--wire-color-info);
}
.wire-stats-bar__item[data-color="success"] {
--item-accent: var(--wire-color-success);
}
.wire-stats-bar__item[data-color="warning"] {
--item-accent: var(--wire-color-warning);
}
.wire-stats-bar__item[data-color="danger"] {
--item-accent: var(--wire-color-danger);
}
.wire-stats-bar__item:hover {
background: color-mix(in srgb, var(--item-accent) 6%, transparent);
}
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__item:hover {
background: color-mix(in srgb, white 8%, transparent);
}
.wire-stats-bar__content {
min-width: 0;
}
.wire-stats-bar__label {
margin: 0;
color: var(--wire-color-text-muted);
font-size: 0.78rem;
font-weight: 600;
line-height: 1.4;
letter-spacing: 0.01em;
}
.wire-stats-bar__value-row {
display: flex;
align-items: baseline;
gap: 0.25rem;
margin-top: 0.45rem;
}
.wire-stats-bar__value {
margin: 0;
color: var(--wire-color-text);
font-size: clamp(1.75rem, 2.4vw, 2.35rem);
font-weight: 650;
line-height: 1;
letter-spacing: -0.035em;
}
.wire-stats-bar[data-compact="true"] .wire-stats-bar__value {
font-size: clamp(1.45rem, 2vw, 1.9rem);
}
.wire-stats-bar__suffix {
color: var(--item-accent);
font-size: 0.95rem;
font-weight: 700;
}
.wire-stats-bar__description {
max-width: 30rem;
margin: 0.7rem 0 0;
color: var(--wire-color-text-muted);
font-size: 0.78rem;
line-height: 1.55;
}
.wire-stats-bar[data-compact="true"] .wire-stats-bar__description {
margin-top: 0.55rem;
font-size: 0.73rem;
}
.wire-stats-bar__icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.65rem;
height: 2.65rem;
color: var(--item-accent);
background: color-mix(in srgb, var(--item-accent) 11%, transparent);
border-radius: 0.85rem;
}
.wire-stats-bar__icon > span {
width: 1.2rem;
height: 1.2rem;
}
.wire-stats-bar[data-compact="true"] .wire-stats-bar__icon {
width: 2.35rem;
height: 2.35rem;
border-radius: 0.75rem;
}
.wire-stats-bar__trend {
display: inline-flex;
align-items: center;
gap: 0.3rem;
width: fit-content;
margin-top: 0.7rem;
padding: 0.25rem 0.5rem;
color: var(--wire-color-text-muted);
font-size: 0.7rem;
line-height: 1;
background: color-mix(in srgb, var(--wire-color-text) 5%, transparent);
border-radius: 999px;
}
.wire-stats-bar__trend[data-direction="up"] {
color: var(--wire-color-success);
}
.wire-stats-bar__trend[data-direction="down"] {
color: var(--wire-color-danger);
}
.wire-stats-bar__trend-icon {
width: 0.8rem;
height: 0.8rem;
}
.wire-stats-bar__trend-value {
font-weight: 700;
}
.wire-stats-bar__trend-label {
opacity: 0.8;
}
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__label,
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__description,
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__value,
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__suffix {
color: var(--stats-contrast);
}
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__label,
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__description {
opacity: 0.78;
}
.wire-stats-bar[data-variant="solid"] .wire-stats-bar__icon {
color: var(--stats-contrast);
background: color-mix(in srgb, white 13%, transparent);
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item + .wire-stats-bar__item {
border-top: 1px solid var(--wire-color-border);
}
.wire-stats-bar[data-variant="solid"][data-dividers="true"]
.wire-stats-bar__item + .wire-stats-bar__item {
border-color: color-mix(in srgb, white 18%, transparent);
}
@media (min-width: 640px) {
.wire-stats-bar__surface {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.wire-stats-bar[data-columns="1"] .wire-stats-bar__surface {
grid-template-columns: minmax(0, 1fr);
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item {
border-top: 0;
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item:nth-child(even) {
border-left: 1px solid var(--wire-color-border);
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item:nth-child(n + 3) {
border-top: 1px solid var(--wire-color-border);
}
.wire-stats-bar[data-variant="solid"][data-dividers="true"]
.wire-stats-bar__item:nth-child(even),
.wire-stats-bar[data-variant="solid"][data-dividers="true"]
.wire-stats-bar__item:nth-child(n + 3) {
border-color: color-mix(in srgb, white 18%, transparent);
}
}
@media (min-width: 1024px) {
.wire-stats-bar[data-columns="1"] .wire-stats-bar__surface {
grid-template-columns: minmax(0, 1fr);
}
.wire-stats-bar[data-columns="2"] .wire-stats-bar__surface {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.wire-stats-bar[data-columns="3"] .wire-stats-bar__surface {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.wire-stats-bar[data-columns="4"] .wire-stats-bar__surface {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.wire-stats-bar[data-columns="5"] .wire-stats-bar__surface {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.wire-stats-bar[data-columns="6"] .wire-stats-bar__surface {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item,
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item:nth-child(even),
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item:nth-child(n + 3) {
border-top: 0;
border-left: 1px solid var(--wire-color-border);
}
.wire-stats-bar[data-dividers="true"] .wire-stats-bar__item:first-child {
border-left: 0;
}
.wire-stats-bar[data-variant="solid"][data-dividers="true"] .wire-stats-bar__item {
border-color: color-mix(in srgb, white 18%, transparent);
}
}
@media (max-width: 639px) {
.wire-stats-bar[data-max-width="full"] .wire-stats-bar__surface {
border-inline: 0;
border-radius: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-stats-bar__item {
transition: none;
}
}
}
}
+322 -10
View File
@@ -1,21 +1,333 @@
component Tooltip {
props {
@event toggle = function
@event open = function
@event close = function
id = ""
open = false
defaultOpen = false
title = ""
description = ""
content = "Tooltip"
trigger = "hover"
placement = "top"
size = "default"
color = "primary"
title = "Tooltip"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
variant = "dark"
maxWidth = "18rem"
offset = 10
showArrow = true
interactive = false
disabled = false
class = ""
}
state visible = defaultOpen
functions {
function isOpen() {
return open || visible
}
function showTooltip(reason, sourceEvent) {
if (disabled || trigger === "manual") {
return
}
visible = true
$emit("open", { reason: reason, sourceEvent: sourceEvent })
$emit("toggle", { open: true, reason: reason, sourceEvent: sourceEvent })
}
function hideTooltip(reason, sourceEvent) {
if (trigger === "manual") {
return
}
visible = false
$emit("close", { reason: reason, sourceEvent: sourceEvent })
$emit("toggle", { open: false, reason: reason, sourceEvent: sourceEvent })
}
function toggleTooltip(sourceEvent) {
if (isOpen()) {
hideTooltip("click", sourceEvent)
} else {
showTooltip("click", sourceEvent)
}
}
function handleKeydown(sourceEvent) {
if (sourceEvent.key === "Escape") {
sourceEvent.preventDefault()
hideTooltip("escape", sourceEvent)
}
}
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--tooltip wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
<span
{...attrs}
data-ui-component="Tooltip"
data-open='{open || visible ? "true" : "false"}'
data-trigger='{trigger}'
data-placement='{placement}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
data-interactive='{interactive ? "true" : "false"}'
class='wire-tooltip {class}'
style='--wire-tooltip-max-width:{maxWidth};--wire-tooltip-offset:{offset}px;'
@mouseenter='if (trigger === "hover" || trigger === "both") { showTooltip("hover", event) }'
@mouseleave='if (trigger === "hover" || trigger === "both") { hideTooltip("hover", event) }'
@focusin='if (trigger === "focus" || trigger === "hover" || trigger === "both") { showTooltip("focus", event) }'
@focusout='if (trigger === "focus" || trigger === "hover" || trigger === "both") { hideTooltip("focus", event) }'
@keydown='handleKeydown(event)'
>
<span
class="wire-tooltip__trigger"
tabindex='{disabled ? "-1" : "0"}'
aria-describedby='{(open || visible) && id ? id : ""}'
@click='if (trigger === "click" || trigger === "both") { toggleTooltip(event) }'
>
<slot name="trigger"></slot>
</span>
<span
id='{id}'
class="wire-tooltip__content"
data-show='{open || visible}'
role="tooltip"
>
{#if showArrow}
<span class="wire-tooltip__arrow" aria-hidden="true"></span>
{/if}
{#if title}
<strong>{title}</strong>
{/if}
{#if description}
<span class="wire-tooltip__description">{description}</span>
{:else if content}
<span class="wire-tooltip__description">{content}</span>
{/if}
<slot></slot>
</span>
</span>
}
style {
.wire-tooltip {
--tooltip-accent: var(--wire-color-primary);
--tooltip-soft: var(--wire-color-primary-soft);
position: relative;
display: inline-flex;
max-width: 100%;
}
.wire-tooltip[data-color="secondary"] {
--tooltip-accent: var(--wire-color-secondary);
--tooltip-soft: var(--wire-color-secondary-soft);
}
.wire-tooltip[data-color="info"] {
--tooltip-accent: var(--wire-color-info);
--tooltip-soft: var(--wire-color-info-soft);
}
.wire-tooltip[data-color="success"] {
--tooltip-accent: var(--wire-color-success);
--tooltip-soft: var(--wire-color-success-soft);
}
.wire-tooltip[data-color="warning"] {
--tooltip-accent: var(--wire-color-warning);
--tooltip-soft: var(--wire-color-warning-soft);
}
.wire-tooltip[data-color="danger"] {
--tooltip-accent: var(--wire-color-danger);
--tooltip-soft: var(--wire-color-danger-soft);
}
.wire-tooltip__trigger {
display: inline-flex;
max-width: 100%;
outline: none;
}
.wire-tooltip__trigger:focus-visible {
border-radius: 0.4rem;
outline: 2px solid var(--wire-color-focus);
outline-offset: 3px;
}
.wire-tooltip__content {
position: absolute;
z-index: 1300;
left: 50%;
bottom: calc(100% + var(--wire-tooltip-offset));
display: grid;
gap: 0.22rem;
width: max-content;
max-width: min(var(--wire-tooltip-max-width), calc(100vw - 1rem));
padding: 0.58rem 0.72rem;
color: white;
background: color-mix(in srgb, black 88%, var(--tooltip-accent) 12%);
border: 1px solid color-mix(in srgb, white 12%, transparent);
border-radius: 0.65rem;
box-shadow: 0 14px 38px color-mix(in srgb, black 28%, transparent);
font-size: 0.73rem;
line-height: 1.45;
text-align: left;
transform: translateX(-50%);
pointer-events: none;
}
.wire-tooltip[data-interactive="true"] .wire-tooltip__content {
pointer-events: auto;
}
.wire-tooltip[data-variant="soft"] .wire-tooltip__content {
color: var(--wire-color-text);
background: var(--tooltip-soft);
border-color: color-mix(in srgb, var(--tooltip-accent) 24%, var(--wire-color-border));
}
.wire-tooltip[data-variant="light"] .wire-tooltip__content {
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border-color: var(--wire-color-border);
}
.wire-tooltip[data-variant="solid"] .wire-tooltip__content {
color: var(--wire-color-primary-contrast);
background: var(--tooltip-accent);
border-color: color-mix(in srgb, white 18%, transparent);
}
.wire-tooltip__content strong {
font-size: 0.75rem;
font-weight: 650;
}
.wire-tooltip__description {
color: color-mix(in srgb, currentColor 82%, transparent);
}
.wire-tooltip__arrow {
position: absolute;
left: 50%;
bottom: -0.32rem;
width: 0.62rem;
height: 0.62rem;
background: inherit;
border-right: 1px solid color-mix(in srgb, white 10%, transparent);
border-bottom: 1px solid color-mix(in srgb, white 10%, transparent);
transform: translateX(-50%) rotate(45deg);
}
.wire-tooltip[data-placement="bottom"] .wire-tooltip__content {
top: calc(100% + var(--wire-tooltip-offset));
bottom: auto;
}
.wire-tooltip[data-placement="bottom"] .wire-tooltip__arrow {
top: -0.32rem;
bottom: auto;
transform: translateX(-50%) rotate(225deg);
}
.wire-tooltip[data-placement="left"] .wire-tooltip__content {
top: 50%;
right: calc(100% + var(--wire-tooltip-offset));
bottom: auto;
left: auto;
transform: translateY(-50%);
}
.wire-tooltip[data-placement="left"] .wire-tooltip__arrow {
top: 50%;
right: -0.32rem;
bottom: auto;
left: auto;
transform: translateY(-50%) rotate(-45deg);
}
.wire-tooltip[data-placement="right"] .wire-tooltip__content {
top: 50%;
bottom: auto;
left: calc(100% + var(--wire-tooltip-offset));
transform: translateY(-50%);
}
.wire-tooltip[data-placement="right"] .wire-tooltip__arrow {
top: 50%;
bottom: auto;
left: -0.32rem;
transform: translateY(-50%) rotate(135deg);
}
.wire-tooltip[data-placement="top-start"] .wire-tooltip__content,
.wire-tooltip[data-placement="bottom-start"] .wire-tooltip__content {
left: 0;
transform: none;
}
.wire-tooltip[data-placement="top-start"] .wire-tooltip__arrow,
.wire-tooltip[data-placement="bottom-start"] .wire-tooltip__arrow {
left: 1rem;
transform: rotate(45deg);
}
.wire-tooltip[data-placement="bottom-start"] .wire-tooltip__arrow {
transform: rotate(225deg);
}
.wire-tooltip[data-placement="top-end"] .wire-tooltip__content,
.wire-tooltip[data-placement="bottom-end"] .wire-tooltip__content {
right: 0;
left: auto;
transform: none;
}
.wire-tooltip[data-placement="top-end"] .wire-tooltip__arrow,
.wire-tooltip[data-placement="bottom-end"] .wire-tooltip__arrow {
right: 1rem;
left: auto;
transform: rotate(45deg);
}
.wire-tooltip[data-placement="bottom-end"] .wire-tooltip__arrow {
transform: rotate(225deg);
}
.wire-tooltip[data-size="sm"] .wire-tooltip__content {
padding: 0.45rem 0.58rem;
font-size: 0.68rem;
}
.wire-tooltip[data-size="lg"] .wire-tooltip__content {
padding: 0.72rem 0.85rem;
font-size: 0.8rem;
}
@media (max-width: 639px) {
.wire-tooltip__content {
position: fixed;
right: 0.75rem;
bottom: 0.75rem;
left: 0.75rem;
top: auto;
width: auto;
max-width: none;
transform: none;
}
.wire-tooltip__arrow {
display: none;
}
}
}
}