Build once. Ship everywhere.
Accessible building blocks that feel at home in every product.
Slide carousel with touch gestures and previous and next controls.
Move between stories with the arrows or select a slide with the dots.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Focus stops rotation until Start is pressed. Hover pauses temporarily.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
Accessible building blocks that feel at home in every product.
Clear patterns, composed from small and reliable pieces.
Useful, keyboard-ready interfaces before JavaScript loads.
"use client";
import { CarouselClient } from "@gunkin/uiify/components/carousel/client";
const items = ["One", "Two", "Three"].map((label) => ({
id: label,
label,
className: "",
children: <div className="">{label}</div>,
}));
export default function Example() {
return <CarouselClient id="featured" aria-label="Featured cards" className="" items={items} />;
}/* Optional carousel skin. The native scrollbar is only hidden once real controls replace it. */
@layer components {
[data-uiify-carousel] {
position: relative;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-sm);
inline-size: 100%;
max-inline-size: 40rem;
}
/* Full-width rows: an optional rotation button, the viewport, and the status text. */
[data-uiify-carousel] > [data-part="viewport"],
[data-uiify-carousel] > [data-part="status"],
[data-uiify-carousel] > button[data-part="controls"][data-carousel-action="rotation"] {
grid-column: 1 / -1;
}
/*
* Hide the scrollbar only once the carousel is attached AND renders buttons that can
* take over from it. Without scripting the scrollbar is the sole indication that the
* content scrolls at all, so the static carousel keeps it. `scrollbar-width` alone is
* enough for the supported engines — no prefixed fallback.
*/
[data-uiify-carousel][data-enhanced]:has(> button[data-part="controls"])
> [data-part="viewport"] {
scrollbar-width: none;
}
[data-uiify-carousel]
> [data-part="viewport"]
> [data-part="slide"]
> [data-part="slide-content"] {
display: flex;
container-type: inline-size;
min-block-size: 18rem;
aspect-ratio: 1;
align-items: center;
justify-content: center;
border: var(--border-width) solid var(--border);
border-radius: var(--radius-xl);
corner-shape: squircle;
background-color: var(--card);
color: var(--foreground);
font-size: 2.25rem;
font-weight: var(--weight-semibold);
line-height: 1;
box-shadow: var(--shadow-popover);
}
/* The controls are Button-skinned (`outline`; previous/next at the icon size). */
[data-uiify-carousel] > button[data-part="controls"][data-carousel-action="rotation"] {
justify-self: start;
margin-block-end: var(--space-sm);
}
[data-uiify-carousel] > button[data-part="controls"][data-carousel-action="previous"],
[data-uiify-carousel] > button[data-part="controls"][data-carousel-action="next"] {
font-size: 1.25rem;
line-height: 1;
}
[data-uiify-carousel] > [data-part="dots"] > a:focus-visible {
outline: none;
box-shadow: 0 0 0 var(--ring-width) color-mix(in oklch, var(--ring) 16%, transparent);
}
[data-uiify-carousel] > [data-part="dots"] {
/* Explicit middle column: pagination stays centred whether or not the
previous/next buttons exist to occupy the outer columns. */
grid-column: 2;
justify-self: stretch;
display: flex;
justify-content: center;
gap: var(--space-xs);
}
[data-uiify-carousel] > [data-part="dots"] > a {
display: inline-grid;
place-items: center;
inline-size: 2.75rem;
block-size: 2.75rem;
border-radius: var(--radius-full);
background-color: transparent;
color: var(--muted-foreground);
text-decoration: none;
transition: var(--transition-colors);
font-size: 0;
}
[data-uiify-carousel] > [data-part="dots"] > a::after {
content: "";
inline-size: 0.5rem;
block-size: 0.5rem;
border-radius: 50%;
background: var(--input);
}
[data-uiify-carousel] > [data-part="dots"] > a[aria-current="true"]::after {
background: var(--primary);
}
}
@layer components {
[data-uiify-carousel] [data-part="story"] {
display: grid;
grid-template-rows: auto 1fr auto;
inline-size: 100%;
block-size: 100%;
min-inline-size: 0;
min-block-size: 0;
padding: clamp(0.875rem, 4cqi, 1.5rem);
text-align: start;
}
[data-uiify-carousel] [data-part="story-topline"],
[data-uiify-carousel] [data-part="story-footer"] {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 0.5rem;
color: var(--muted-foreground);
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.08em;
}
[data-uiify-carousel] [data-part="story-body"] {
display: grid;
align-content: center;
gap: 0.75rem;
min-inline-size: 0;
padding-block: 1rem;
}
[data-uiify-carousel] [data-part="story-eyebrow"] {
color: var(--primary);
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.1em;
}
[data-uiify-carousel] [data-part="story-body"] > h3 {
max-inline-size: 12ch;
margin: 0;
color: var(--foreground);
font-size: clamp(1.25rem, 6cqi, 2rem);
font-weight: 600;
letter-spacing: -0.045em;
line-height: 1.08;
white-space: pre-line;
}
[data-uiify-carousel] [data-part="story-body"] > p {
max-inline-size: 30ch;
margin: 0;
color: var(--muted-foreground);
font-size: 0.875rem;
line-height: 1.55;
}
}"use client";
import { CarouselClient } from "@gunkin/uiify/components/carousel/client";
const items = ["One", "Two", "Three"].map((label) => ({
id: label,
label,
className: "",
children: <div className="">{label}</div>,
}));
export default function Example() {
return (
<CarouselClient
id="featured"
aria-label="Featured cards"
className={
"relative grid [grid-template-columns:auto_1fr_auto] items-center gap-sm [inline-size:100%] [max-inline-size" +
":40rem] [&>button[data-part=controls]]:relative [&>button[data-part=controls]]:isolate [&>button[data-part=" +
"controls]]:inline-flex [&>button[data-part=controls]]:items-center [&>button[data-part=controls]]:justify-c" +
"enter [&>button[data-part=controls][hidden]]:hidden [&>button[data-part=controls]]:min-h-11 [&>button[data-" +
"part=controls]]:min-w-11 [&>button[data-part=controls]]:gap-1.5 [&>button[data-part=controls]]:px-5 [&>butt" +
"on[data-part=controls]]:py-0 [&>button[data-part=controls]]:border-[length:var(--border-width)] [&>button[d" +
"ata-part=controls]]:border-solid [&>button[data-part=controls]]:[border-color:color-mix(in_oklch,_var(--for" +
"eground)_20%,_transparent)] [&>button[data-part=controls]]:rounded-full [&>button[data-part=controls]]:bg-t" +
"ransparent [&>button[data-part=controls]]:bg-clip-padding [&>button[data-part=controls]]:text-foreground [&" +
">button[data-part=controls]]:font-medium [&>button[data-part=controls]]:text-base [&>button[data-part=contr" +
"ols]]:leading-6 [&>button[data-part=controls]]:[font-family:inherit] [&>button[data-part=controls]]:no-unde" +
"rline [&>button[data-part=controls]]:cursor-pointer [&>button[data-part=controls]]:[transition:var(--transi" +
"tion-colors)] [&>button[data-part=controls]]:disabled:opacity-50 [&>button[data-part=controls]]:disabled:cu" +
"rsor-not-allowed [&>button[data-part=controls]]:focus-visible:outline-none [&>button[data-part=controls]]:f" +
"ocus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [@media" +
"_(any-hover:_hover)_and_(any-pointer:_fine)]:[&>button[data-part=controls]:hover:not(:disabled)]:[backgroun" +
"d-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] [&>button[data-part=controls]:active:not(:" +
"disabled)]:[background-color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] [&>button[data-part=c" +
"ontrols]]:before:content-none [&>button[data-part=controls]]:after:content-[''] [&>button[data-part=control" +
"s]]:after:absolute [&>button[data-part=controls]]:after:[inset:calc(-1_*_var(--border-width))] [&>button[da" +
"ta-part=controls]]:after:z-[-1] [&>button[data-part=controls]]:after:rounded-[inherit] [&>button[data-part=" +
"controls]]:after:pointer-events-none [&>button[data-part=controls]]:after:[padding:var(--border-width)] [&>" +
"button[data-part=controls]]:after:[background-image:linear-gradient(to_bottom,_color-mix(in_oklch,_var(--fo" +
"reground)_25%,_transparent),_transparent_75%)] [&>button[data-part=controls]]:after:[mask:linear-gradient(#" +
"fff_0_0)_content-box_exclude,_linear-gradient(#fff_0_0)] [&>[data-part=viewport]]:[grid-column:1_/_-1] [&>[" +
"data-part=status]]:[grid-column:1_/_-1] [&>button[data-part=controls][data-carousel-action=rotation]]:[grid" +
"-column:1_/_-1] data-[enhanced]:[&:has(>button[data-part=controls])>[data-part=viewport]]:[scrollbar-width:" +
"none] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:flex [&>[data-part=viewport]>[da" +
"ta-part=slide]>[data-part=slide-content]]:[container-type:inline-size] [&>[data-part=viewport]>[data-part=s" +
"lide]>[data-part=slide-content]]:[min-block-size:18rem] [&>[data-part=viewport]>[data-part=slide]>[data-par" +
"t=slide-content]]:[aspect-ratio:1] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:ite" +
"ms-center [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:justify-center [&>[data-part" +
"=viewport]>[data-part=slide]>[data-part=slide-content]]:border-[length:var(--border-width)] [&>[data-part=v" +
"iewport]>[data-part=slide]>[data-part=slide-content]]:border-solid [&>[data-part=viewport]>[data-part=slide" +
"]>[data-part=slide-content]]:border-border [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-conte" +
"nt]]:rounded-xl [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[corner-shape:squircle" +
"] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:bg-card [&>[data-part=viewport]>[dat" +
"a-part=slide]>[data-part=slide-content]]:text-foreground [&>[data-part=viewport]>[data-part=slide]>[data-pa" +
"rt=slide-content]]:[font-size:2.25rem] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]" +
":font-[number:var(--weight-semibold)] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:" +
"leading-none [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:shadow-popover [&>button[" +
"data-part=controls][data-carousel-action=rotation]]:[justify-self:start] [&>button[data-part=controls][data" +
"-carousel-action=rotation]]:[margin-block-end:var(--space-sm)] [&>button[data-part=controls][data-carousel-" +
"action=previous]]:[font-size:1.25rem] [&>button[data-part=controls][data-carousel-action=previous]]:leading" +
"-none [&>button[data-part=controls][data-carousel-action=next]]:[font-size:1.25rem] [&>button[data-part=con" +
"trols][data-carousel-action=next]]:leading-none [&>[data-part=dots]>a:focus-visible]:[outline:none] [&>[dat" +
"a-part=dots]>a:focus-visible]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_tran" +
"sparent)] [&>[data-part=dots]]:[grid-column:2] [&>[data-part=dots]]:[justify-self:stretch] [&>[data-part=do" +
"ts]]:flex [&>[data-part=dots]]:justify-center [&>[data-part=dots]]:gap-xs [&>[data-part=dots]>a]:inline-gri" +
"d [&>[data-part=dots]>a]:place-items-center [&>[data-part=dots]>a]:[inline-size:2.75rem] [&>[data-part=dots" +
"]>a]:[block-size:2.75rem] [&>[data-part=dots]>a]:rounded-[var(--radius-full)] [&>[data-part=dots]>a]:bg-tra" +
"nsparent [&>[data-part=dots]>a]:text-muted-foreground [&>[data-part=dots]>a]:no-underline [&>[data-part=dot" +
"s]>a]:[transition:var(--transition-colors)] [&>[data-part=dots]>a]:[font-size:0] [&>[data-part=dots]>a::aft" +
"er]:content-[''] [&>[data-part=dots]>a::after]:[inline-size:0.5rem] [&>[data-part=dots]>a::after]:[block-si" +
"ze:0.5rem] [&>[data-part=dots]>a::after]:rounded-[50%] [&>[data-part=dots]>a::after]:bg-input [&>[data-part" +
"=dots]>a[aria-current=true]::after]:bg-primary [&_[data-part=story]]:grid [&_[data-part=story]]:[grid-templ" +
"ate-rows:auto_1fr_auto] [&_[data-part=story]]:[inline-size:100%] [&_[data-part=story]]:[block-size:100%] [&" +
"_[data-part=story]]:[min-inline-size:0] [&_[data-part=story]]:[min-block-size:0] [&_[data-part=story]]:[pad" +
"ding:clamp(0.875rem,_4cqi,_1.5rem)] [&_[data-part=story]]:text-start [&_[data-part=story-topline]]:flex [&_" +
"[data-part=story-topline]]:items-center [&_[data-part=story-topline]]:justify-between [&_[data-part=story-t" +
"opline]]:flex-wrap [&_[data-part=story-topline]]:[gap:0.5rem] [&_[data-part=story-topline]]:text-muted-fore" +
"ground [&_[data-part=story-topline]]:[font-family:ui-monospace,_\"SF_Mono\",_Menlo,_Consolas,_monospace] [&_[" +
"data-part=story-topline]]:[font-size:0.75rem] [&_[data-part=story-topline]]:[font-weight:500] [&_[data-part" +
"=story-topline]]:[letter-spacing:0.08em] [&_[data-part=story-footer]]:flex [&_[data-part=story-footer]]:ite" +
"ms-center [&_[data-part=story-footer]]:justify-between [&_[data-part=story-footer]]:flex-wrap [&_[data-part" +
"=story-footer]]:[gap:0.5rem] [&_[data-part=story-footer]]:text-muted-foreground [&_[data-part=story-footer]" +
"]:[font-family:ui-monospace,_\"SF_Mono\",_Menlo,_Consolas,_monospace] [&_[data-part=story-footer]]:[font-size" +
":0.75rem] [&_[data-part=story-footer]]:[font-weight:500] [&_[data-part=story-footer]]:[letter-spacing:0.08e" +
"m] [&_[data-part=story-body]]:grid [&_[data-part=story-body]]:[align-content:center] [&_[data-part=story-bo" +
"dy]]:[gap:0.75rem] [&_[data-part=story-body]]:[min-inline-size:0] [&_[data-part=story-body]]:[padding-block" +
":1rem] [&_[data-part=story-eyebrow]]:text-primary [&_[data-part=story-eyebrow]]:[font-size:0.75rem] [&_[dat" +
"a-part=story-eyebrow]]:[font-weight:600] [&_[data-part=story-eyebrow]]:[letter-spacing:0.1em] [&_[data-part" +
"=story-body]>h3]:[max-inline-size:12ch] [&_[data-part=story-body]>h3]:m-0 [&_[data-part=story-body]>h3]:tex" +
"t-foreground [&_[data-part=story-body]>h3]:[font-size:clamp(1.25rem,_6cqi,_2rem)] [&_[data-part=story-body]" +
">h3]:[font-weight:600] [&_[data-part=story-body]>h3]:[letter-spacing:-0.045em] [&_[data-part=story-body]>h3" +
"]:[line-height:1.08] [&_[data-part=story-body]>h3]:[white-space:pre-line] [&_[data-part=story-body]>p]:[max" +
"-inline-size:30ch] [&_[data-part=story-body]>p]:m-0 [&_[data-part=story-body]>p]:text-muted-foreground [&_[" +
"data-part=story-body]>p]:[font-size:0.875rem] [&_[data-part=story-body]>p]:[line-height:1.55]"
}
items={items}
/>
);
}/* app.css */
@layer reset, ui, app, tokens, base, demo, components, utilities, theme;
@import "@gunkin/uiify/styles/reset";
@import "@gunkin/uiify/components/behavior.css";
@import "@gunkin/uiify/styles/tokens";
@import "@gunkin/uiify/styles/tailwind";
@import "tailwindcss/utilities.css";
@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }
@source inline('relative grid [grid-template-columns:auto_1fr_auto] items-center gap-sm [inline-size:100%] [max-inline-size:40rem] [&>button[data-part=controls]]:relative [&>button[data-part=controls]]:isolate [&>button[data-part=controls]]:inline-flex [&>button[data-part=controls]]:items-center [&>button[data-part=controls]]:justify-center [&>button[data-part=controls][hidden]]:hidden [&>button[data-part=controls]]:min-h-11 [&>button[data-part=controls]]:min-w-11 [&>button[data-part=controls]]:gap-1.5 [&>button[data-part=controls]]:px-5 [&>button[data-part=controls]]:py-0 [&>button[data-part=controls]]:border-[length:var(--border-width)] [&>button[data-part=controls]]:border-solid [&>button[data-part=controls]]:[border-color:color-mix(in_oklch,_var(--foreground)_20%,_transparent)] [&>button[data-part=controls]]:rounded-full [&>button[data-part=controls]]:bg-transparent [&>button[data-part=controls]]:bg-clip-padding [&>button[data-part=controls]]:text-foreground [&>button[data-part=controls]]:font-medium [&>button[data-part=controls]]:text-base [&>button[data-part=controls]]:leading-6 [&>button[data-part=controls]]:[font-family:inherit] [&>button[data-part=controls]]:no-underline [&>button[data-part=controls]]:cursor-pointer [&>button[data-part=controls]]:[transition:var(--transition-colors)] [&>button[data-part=controls]]:disabled:opacity-50 [&>button[data-part=controls]]:disabled:cursor-not-allowed [&>button[data-part=controls]]:focus-visible:outline-none [&>button[data-part=controls]]:focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[&>button[data-part=controls]:hover:not(:disabled)]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] [&>button[data-part=controls]:active:not(:disabled)]:[background-color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] [&>button[data-part=controls]]:before:content-none [&>button[data-part=controls]]:after:absolute [&>button[data-part=controls]]:after:[inset:calc(-1_*_var(--border-width))] [&>button[data-part=controls]]:after:z-[-1] [&>button[data-part=controls]]:after:rounded-[inherit] [&>button[data-part=controls]]:after:pointer-events-none [&>button[data-part=controls]]:after:[padding:var(--border-width)] [&>button[data-part=controls]]:after:[background-image:linear-gradient(to_bottom,_color-mix(in_oklch,_var(--foreground)_25%,_transparent),_transparent_75%)] [&>button[data-part=controls]]:after:[mask:linear-gradient(#fff_0_0)_content-box_exclude,_linear-gradient(#fff_0_0)] [&>[data-part=viewport]]:[grid-column:1_/_-1] [&>[data-part=status]]:[grid-column:1_/_-1] [&>button[data-part=controls][data-carousel-action=rotation]]:[grid-column:1_/_-1] data-[enhanced]:[&:has(>button[data-part=controls])>[data-part=viewport]]:[scrollbar-width:none] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:flex [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[container-type:inline-size] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[min-block-size:18rem] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[aspect-ratio:1] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:items-center [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:justify-center [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:border-[length:var(--border-width)] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:border-solid [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:border-border [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:rounded-xl [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[corner-shape:squircle] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:bg-card [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:text-foreground [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:[font-size:2.25rem] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:font-[number:var(--weight-semibold)] [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:leading-none [&>[data-part=viewport]>[data-part=slide]>[data-part=slide-content]]:shadow-popover [&>button[data-part=controls][data-carousel-action=rotation]]:[justify-self:start] [&>button[data-part=controls][data-carousel-action=rotation]]:[margin-block-end:var(--space-sm)] [&>button[data-part=controls][data-carousel-action=previous]]:[font-size:1.25rem] [&>button[data-part=controls][data-carousel-action=previous]]:leading-none [&>button[data-part=controls][data-carousel-action=next]]:[font-size:1.25rem] [&>button[data-part=controls][data-carousel-action=next]]:leading-none [&>[data-part=dots]>a:focus-visible]:[outline:none] [&>[data-part=dots]>a:focus-visible]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [&>[data-part=dots]]:[grid-column:2] [&>[data-part=dots]]:[justify-self:stretch] [&>[data-part=dots]]:flex [&>[data-part=dots]]:justify-center [&>[data-part=dots]]:gap-xs [&>[data-part=dots]>a]:inline-grid [&>[data-part=dots]>a]:place-items-center [&>[data-part=dots]>a]:[inline-size:2.75rem] [&>[data-part=dots]>a]:[block-size:2.75rem] [&>[data-part=dots]>a]:rounded-[var(--radius-full)] [&>[data-part=dots]>a]:bg-transparent [&>[data-part=dots]>a]:text-muted-foreground [&>[data-part=dots]>a]:no-underline [&>[data-part=dots]>a]:[transition:var(--transition-colors)] [&>[data-part=dots]>a]:[font-size:0] [&>[data-part=dots]>a::after]:[inline-size:0.5rem] [&>[data-part=dots]>a::after]:[block-size:0.5rem] [&>[data-part=dots]>a::after]:rounded-[50%] [&>[data-part=dots]>a::after]:bg-input [&>[data-part=dots]>a[aria-current=true]::after]:bg-primary [&_[data-part=story]]:grid [&_[data-part=story]]:[grid-template-rows:auto_1fr_auto] [&_[data-part=story]]:[inline-size:100%] [&_[data-part=story]]:[block-size:100%] [&_[data-part=story]]:[min-inline-size:0] [&_[data-part=story]]:[min-block-size:0] [&_[data-part=story]]:[padding:clamp(0.875rem,_4cqi,_1.5rem)] [&_[data-part=story]]:text-start [&_[data-part=story-topline]]:flex [&_[data-part=story-topline]]:items-center [&_[data-part=story-topline]]:justify-between [&_[data-part=story-topline]]:flex-wrap [&_[data-part=story-topline]]:[gap:0.5rem] [&_[data-part=story-topline]]:text-muted-foreground [&_[data-part=story-topline]]:[font-family:ui-monospace,_"SF_Mono",_Menlo,_Consolas,_monospace] [&_[data-part=story-topline]]:[font-size:0.75rem] [&_[data-part=story-topline]]:[font-weight:500] [&_[data-part=story-topline]]:[letter-spacing:0.08em] [&_[data-part=story-footer]]:flex [&_[data-part=story-footer]]:items-center [&_[data-part=story-footer]]:justify-between [&_[data-part=story-footer]]:flex-wrap [&_[data-part=story-footer]]:[gap:0.5rem] [&_[data-part=story-footer]]:text-muted-foreground [&_[data-part=story-footer]]:[font-family:ui-monospace,_"SF_Mono",_Menlo,_Consolas,_monospace] [&_[data-part=story-footer]]:[font-size:0.75rem] [&_[data-part=story-footer]]:[font-weight:500] [&_[data-part=story-footer]]:[letter-spacing:0.08em] [&_[data-part=story-body]]:grid [&_[data-part=story-body]]:[align-content:center] [&_[data-part=story-body]]:[gap:0.75rem] [&_[data-part=story-body]]:[min-inline-size:0] [&_[data-part=story-body]]:[padding-block:1rem] [&_[data-part=story-eyebrow]]:text-primary [&_[data-part=story-eyebrow]]:[font-size:0.75rem] [&_[data-part=story-eyebrow]]:[font-weight:600] [&_[data-part=story-eyebrow]]:[letter-spacing:0.1em] [&_[data-part=story-body]>h3]:[max-inline-size:12ch] [&_[data-part=story-body]>h3]:m-0 [&_[data-part=story-body]>h3]:text-foreground [&_[data-part=story-body]>h3]:[font-size:clamp(1.25rem,_6cqi,_2rem)] [&_[data-part=story-body]>h3]:[font-weight:600] [&_[data-part=story-body]>h3]:[letter-spacing:-0.045em] [&_[data-part=story-body]>h3]:[line-height:1.08] [&_[data-part=story-body]>h3]:[white-space:pre-line] [&_[data-part=story-body]>p]:[max-inline-size:30ch] [&_[data-part=story-body]>p]:m-0 [&_[data-part=story-body]>p]:text-muted-foreground [&_[data-part=story-body]>p]:[font-size:0.875rem] [&_[data-part=story-body]>p]:[line-height:1.55]');
@source inline("[&>button[data-part=controls]]:after:content-[''] [&>[data-part=dots]>a::after]:content-['']");
@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }
/* Authored animation and registered properties used by the Tailwind skin.
* Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
@keyframes uiify-presence-in {
from {
opacity: 0;
scale: var(--presence-scale);
}
}
}
@layer components {
@keyframes uiify-presence-out {
to {
opacity: 0;
scale: var(--presence-scale);
}
}
}
@property --uiify-spinner-tail {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@property --uiify-spinner-length {
syntax: "<angle>";
inherits: false;
initial-value: 36deg;
}
@layer components {
@keyframes uiify-spinner-rotate {
to {
transform: rotate(360deg);
}
}
}
@layer components {
@keyframes uiify-spinner-orbit {
0% {
--uiify-spinner-tail: 0deg;
--uiify-spinner-length: 36deg;
}
50% {
--uiify-spinner-tail: 90deg;
--uiify-spinner-length: 324deg;
}
100% {
--uiify-spinner-tail: 360deg;
--uiify-spinner-length: 36deg;
}
}
}
@layer components {
@keyframes uiify-skeleton-pulse {
50% {
opacity: 0.45;
}
}
}
@layer components {
@keyframes uiify-skeleton-shimmer {
to {
background-position: -200% 0;
}
}
}
@layer components {
@keyframes uiify-skeleton-reveal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
@property --candle-body-height {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-body-top {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --candle-slot {
syntax: "<percentage>";
inherits: true;
initial-value: 100%;
}
@property --candle-wick-height {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-wick-top {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --candle-x {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-scale {
syntax: "<number>";
inherits: true;
initial-value: 1;
}
@property --line-y {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --line-prev-y {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --line-count {
syntax: "<integer>";
inherits: false;
initial-value: 1;
}
@property --line-scale {
syntax: "<number>";
inherits: true;
initial-value: 1;
}
@layer components {
@keyframes uiify-otp-input-caret-blink {
50% {
opacity: 0;
}
}
}Carousel| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | Unique root id; used to derive stable fragment destinations. |
items* | readonly CarouselItem[] | — | Items with stable id, accessible label and children. Empty renders null. |
aria-label* | string | — | Accessible carousel name. |
axis | "inline" | "block" | "inline" | Logical scroll axis. Block requires a definite viewport block-size. |
dir | "ltr" | "rtl" | — | Text and inline progression direction. |
navigation | boolean | true | Renders the persistent previous/next buttons in client mode. |
pagination | boolean | true | Direct fragment links. Current state is only published after client attachment. |
labels | Partial<CarouselLabels> | — | Localize previous, next, goTo, navigation, start, stop and of. |
icons | CarouselIcons | — | Visible content for the previous/next buttons, which are empty by default. Pass a glyph, an icon or a word; labels still name them. |
className | string | — | Root class. Style named parts with [data-part] selectors. |
style | CarouselStyle | — | CSS values including slide size, gap and fade duration. |
CarouselClient — @gunkin/uiify/components/carousel/client| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue / value | number | — | Initial or controlled item index; observed settled state drives controls. |
rewind | boolean | false | Wrap finite ends without cloned slides. |
mouseDrag | boolean | false | Opt-in mouse drag. Touch and wheel remain native. |
autoplay | boolean | CarouselAutoplayOptions | false | Delay defaults to 5000ms. Options: delay, paused, cycles and stopOnInteraction. Includes visible Stop/Start. |
onSelect / beforeChange / afterChange / onSwipe | callbacks | — | Report actual settled changes and reasons; see the Carousel guide for controlled and interrupted-motion semantics. |
ref | Ref<CarouselClientHandle> | — | next, previous, goTo, start, stop and reset. |
FadeCarouselClient — @gunkin/uiify/components/carousel/fade| Prop | Type | Default | Description |
|---|---|---|---|
swipe | boolean | true | Touch/pen swipe; mouse requires mouseDrag. Inherits client props except axis. Inactive slides are inert. |
| Key | Action |
|---|---|
| Tab / Enter | Focus and follow native slide links; enhanced buttons activate with Space too |
| Arrow keys | Use native scrolling while the viewport is focused |
The base Carousel is a server component with native scrolling, snapping and finite fragment navigation. The interactive /client entry is Tier 2: synchronized dots, persistent arrows, callbacks, autoplay and mouse drag require hydration; /fade adds CSS opacity transitions. Focus stops autoplay until Start, hover pauses temporarily, and reduced motion disables automatic rotation by default. The CSS-generated-controls lab is experimental.
data-uiify-carouseldata-axisdata-enhanceddata-modedata-carousel-indexdata-statedata-dragging--uiify-carousel-slide-size--uiify-carousel-gap--uiify-carousel-fade-duration