From 3f40a240f04a7f121b84dca55f90a897581b6eaf Mon Sep 17 00:00:00 2001 From: Sil Date: Fri, 9 Oct 2026 14:19:31 +0200 Subject: [PATCH 01/60] Apply data-slot conventions to remaining UI components Omit presence flags when false in dropdown menu items and labels, serialize a null dropdown defaultValue as an empty attribute, scope carousel parts to their own root, sync theme toggles after page loads, forward Layout lang and type Video props as iframe attributes. Co-Authored-By: Claude Opus 5.5 --- src/components/ui/carousel/carousel.astro | 17 ++++++++--------- .../dropdown-menu-checkbox-item.astro | 4 ++-- .../ui/dropdown-menu/dropdown-menu-item.astro | 4 ++-- .../ui/dropdown-menu/dropdown-menu-label.astro | 2 +- .../dropdown-menu-radio-item.astro | 4 ++-- .../ui/dropdown-menu/dropdown-menu.astro | 2 +- src/components/ui/layout/layout.astro | 2 +- .../ui/theme-toggle/theme-toggle.astro | 1 + src/components/ui/video/video.astro | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/ui/carousel/carousel.astro b/src/components/ui/carousel/carousel.astro index 4f172130b..b8ec648ce 100644 --- a/src/components/ui/carousel/carousel.astro +++ b/src/components/ui/carousel/carousel.astro @@ -49,26 +49,25 @@ const resolvedOpts: EmblaOptionsType = { } const instances = new Map void>() + // A nested carousel owns its own parts. + const getParts = (root: CarouselRoot, slot: string) => + [...root.querySelectorAll(`[data-slot="${slot}"]`)].filter( + (part) => part.closest('[data-slot="carousel"]') === root + ) const initialize = () => { for (const root of document.querySelectorAll( '[data-slot="carousel"]' )) { if (instances.has(root)) continue - const viewport = root.querySelector( - '[data-slot="carousel-content"]' - ) + const viewport = getParts(root, "carousel-content")[0] if (!viewport) continue const opts: EmblaOptionsType = JSON.parse( root.dataset.carouselOptions || "{}" ) const api = EmblaCarousel(viewport, opts) root.__fulldevCarouselApi = api - const previous = root.querySelectorAll( - '[data-slot="carousel-previous"]' - ) - const next = root.querySelectorAll( - '[data-slot="carousel-next"]' - ) + const previous = getParts(root, "carousel-previous") + const next = getParts(root, "carousel-next") const emit = (type: "carousel:init" | "carousel:select") => { root.dispatchEvent( new CustomEvent(type, { diff --git a/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.astro b/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.astro index 5c53ce747..628a8341d 100644 --- a/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.astro +++ b/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.astro @@ -28,9 +28,9 @@ const { data-slot="dropdown-menu-checkbox-item" data-value={value} data-default-checked={defaultChecked} - data-disabled={disabled} + data-disabled={disabled || undefined} data-variant={variant} - data-inset={inset} + data-inset={inset || undefined} disabled={disabled ? true : undefined} type={type} class={cn( diff --git a/src/components/ui/dropdown-menu/dropdown-menu-item.astro b/src/components/ui/dropdown-menu/dropdown-menu-item.astro index 1870560a1..3602877e9 100644 --- a/src/components/ui/dropdown-menu/dropdown-menu-item.astro +++ b/src/components/ui/dropdown-menu/dropdown-menu-item.astro @@ -24,9 +24,9 @@ const { diff --git a/src/components/ui/dropdown-menu/dropdown-menu-radio-item.astro b/src/components/ui/dropdown-menu/dropdown-menu-radio-item.astro index 3c1424bdb..e4514ec29 100644 --- a/src/components/ui/dropdown-menu/dropdown-menu-radio-item.astro +++ b/src/components/ui/dropdown-menu/dropdown-menu-radio-item.astro @@ -28,9 +28,9 @@ const { data-slot="dropdown-menu-radio-item" data-value={value} data-default-checked={defaultChecked} - data-disabled={disabled} + data-disabled={disabled || undefined} data-variant={variant} - data-inset={inset} + data-inset={inset || undefined} disabled={disabled ? true : undefined} type={type} class={cn( diff --git a/src/components/ui/dropdown-menu/dropdown-menu.astro b/src/components/ui/dropdown-menu/dropdown-menu.astro index 79bb4ef03..a3e3080fe 100644 --- a/src/components/ui/dropdown-menu/dropdown-menu.astro +++ b/src/components/ui/dropdown-menu/dropdown-menu.astro @@ -45,7 +45,7 @@ const defaultValuesAttr =
diff --git a/src/components/ui/theme-toggle/theme-toggle.astro b/src/components/ui/theme-toggle/theme-toggle.astro index d6b776b81..51675c527 100644 --- a/src/components/ui/theme-toggle/theme-toggle.astro +++ b/src/components/ui/theme-toggle/theme-toggle.astro @@ -51,6 +51,7 @@ const { } syncThemeToggles() + document.addEventListener("astro:page-load", syncThemeToggles) const observer = new MutationObserver(syncThemeToggles) diff --git a/src/components/ui/video/video.astro b/src/components/ui/video/video.astro index 6efb69087..0f9cfcabf 100644 --- a/src/components/ui/video/video.astro +++ b/src/components/ui/video/video.astro @@ -3,7 +3,7 @@ import type { HTMLAttributes } from "astro/types" import { cn } from "@/lib/utils" -type Props = Omit, "src"> & { +type Props = Omit, "src"> & { src?: string } From ccaabd8ab8ea31de35a2b2c2fa0a76a96eb78eb0 Mon Sep 17 00:00:00 2001 From: Sil Date: Fri, 9 Oct 2026 14:22:48 +0200 Subject: [PATCH 02/60] Add data-slot markers and resolved data-variant to remaining UI parts Mark Banner container, Logo, LogoText, Marquee, Price, PriceValue, PriceUnit, Rating, Video and Icon with data-slot. Banner, Header and Section default their variant so data-variant reflects the cva default, PriceValue exposes its variant on the root, and Carousel controls emit their resolved data-variant and data-size like Button. Co-Authored-By: Claude Opus 5.5 --- src/components/ui/banner/banner-container.astro | 1 + src/components/ui/banner/banner.astro | 7 ++++++- src/components/ui/carousel/carousel-next.astro | 2 ++ src/components/ui/carousel/carousel-previous.astro | 2 ++ src/components/ui/header/header.astro | 2 +- src/components/ui/icon/icon.astro | 1 + src/components/ui/logo/logo-text.astro | 2 +- src/components/ui/logo/logo.astro | 1 + src/components/ui/marquee/marquee.astro | 1 + src/components/ui/price/price-unit.astro | 1 + src/components/ui/price/price-value.astro | 2 ++ src/components/ui/price/price.astro | 6 +++++- src/components/ui/rating/rating.astro | 1 + src/components/ui/section/section.astro | 2 +- src/components/ui/video/video.astro | 1 + 15 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/ui/banner/banner-container.astro b/src/components/ui/banner/banner-container.astro index e215e5c29..dd2e9a329 100644 --- a/src/components/ui/banner/banner-container.astro +++ b/src/components/ui/banner/banner-container.astro @@ -17,6 +17,7 @@ const { class: className, showClose = true, ...props } = Astro.props showClose && "pr-10", className )} + data-slot="banner-container" {...props} > diff --git a/src/components/ui/banner/banner.astro b/src/components/ui/banner/banner.astro index f4463020d..a2d12fcca 100644 --- a/src/components/ui/banner/banner.astro +++ b/src/components/ui/banner/banner.astro @@ -22,7 +22,12 @@ const variants = cva("cn-banner relative flex items-center", { }, }) -const { class: className, variant, storageKey, ...props } = Astro.props +const { + class: className, + variant = "default", + storageKey, + ...props +} = Astro.props ---