atlas-react-kit 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DESIGN_RULES.md CHANGED
@@ -117,11 +117,11 @@ still read normal words.
117
117
  ## Shape and composition
118
118
 
119
119
  Primitives have square corners. Reserve `rounded-full` for dots, avatars,
120
- other circles, and the toast close badge. Card and dialog titles already use
121
- uppercase, tracked `font-heading` styling. Toasts are overlay popovers with
122
- the same square corners and Alert's left padding. The close control is a
123
- circular badge centred on the top-right corner (half outside the border),
124
- not a control in the text row. A plain `toast()` has no icon — status icons
120
+ and other circles. Card and dialog titles already use
121
+ uppercase, tracked `font-heading` styling. Toasts use the same square corners
122
+ and Alert's left padding, with a square close button inside the top-right
123
+ corner. The toaster floats over the page at its `position`, anchored to the
124
+ viewport; mount one near the app root. A plain `toast()` has no icon — status icons
125
125
  belong to `success`, `info`, `warning`, `error`, and `loading`. Tone colors
126
126
  the icon, title, and border, not the body.
127
127
 
@@ -146,8 +146,8 @@ toasts keep their width.
146
146
 
147
147
  Parents do not force sizes on their children. A vertical `Field` stretches
148
148
  its controls through the flex column, so a control's own width class (such
149
- as `w-32`) wins; `Field`, `Alert`, and `Pagination` set no width of their
150
- own. `FieldGroup` keeps `w-full` because its inline-size container has no
149
+ as `w-32`) wins; `Field` and `Alert` set no width of their
150
+ own. `FieldGroup` and `Pagination` keep `w-full` because an inline-size container has no
151
151
  intrinsic width. Size classes that `className` should be able to replace are
152
152
  plain classes picked from props (`Avatar` `size`, `Separator` orientation,
153
153
  `SelectTrigger` `size`, tab orientation), never `data-*` variants, which sort
package/dist/chart.js CHANGED
@@ -144,7 +144,7 @@ export function ChartYAxisGrid({ frame, yMax, tickCount = 4, format = compactNum
144
144
  const ticks = niceTicks(yMax, tickCount);
145
145
  const top = ticks[ticks.length - 1] ?? 1;
146
146
  const y = (value) => frame.bottom - (value / top) * frame.plotHeight;
147
- return (_jsx("g", { "data-slot": "chart-y-axis", children: ticks.map((tick) => (_jsxs("g", { children: [_jsx("line", { x1: frame.left, y1: y(tick), x2: frame.right, y2: y(tick), strokeWidth: 1, className: "stroke-border" }), _jsx("text", { x: frame.left - 6, y: y(tick) + 3, textAnchor: "end", className: "fill-muted-foreground text-[10px] tabular-nums", children: format(tick) })] }, tick))) }));
147
+ return (_jsx("g", { "data-slot": "chart-y-axis", children: ticks.map((tick) => (_jsxs("g", { children: [_jsx("line", { x1: frame.left, y1: y(tick), x2: frame.right, y2: y(tick), strokeWidth: 1, className: "stroke-input" }), _jsx("text", { x: frame.left - 6, y: y(tick) + 3, textAnchor: "end", className: "fill-muted-foreground text-xs tabular-nums", children: format(tick) })] }, tick))) }));
148
148
  }
149
149
  /**
150
150
  * Readout for the hovered or focused point, placed in percent of the chart
package/dist/field.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMemo } from "react";
4
4
  import { cva } from "class-variance-authority";
5
- import { labelText } from "./internal/styles.js";
5
+ import { errorText, labelText } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  import { Label } from "./label.js";
8
8
  import { Separator } from "./separator.js";
@@ -77,6 +77,6 @@ function FieldError({ className, children, errors, ...props }) {
77
77
  if (!content) {
78
78
  return null;
79
79
  }
80
- return (_jsx("div", { role: "alert", "data-slot": "field-error", className: cn("text-sm font-normal text-destructive", className), ...props, children: content }));
80
+ return (_jsx("div", { role: "alert", "data-slot": "field-error", className: cn(errorText, className), ...props, children: content }));
81
81
  }
82
82
  export { Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegend, FieldSeparator, FieldSet, FieldContent, FieldTitle, };
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { errorText } from "../internal/styles.js";
2
3
  import { cn } from "../utils.js";
3
4
  /** Form-level submit or mutation error (not tied to a single field). */
4
5
  export function FormFeedbackField({ message, className, }) {
5
6
  if (!message)
6
7
  return null;
7
- return (_jsx("p", { "data-slot": "form-feedback", className: cn("text-sm text-destructive", className), role: "alert", children: message }));
8
+ return (_jsx("p", { "data-slot": "form-feedback", className: cn(errorText, className), role: "alert", children: message }));
8
9
  }
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { errorText } from "../internal/styles.js";
2
3
  import { cn } from "../utils.js";
3
4
  /** First TanStack Form error message for a field, if any. */
4
5
  export function getFieldErrorMessage(message) {
@@ -10,5 +11,5 @@ export function FieldError({ id, message, text, className, }) {
10
11
  const resolved = text ?? getFieldErrorMessage(message);
11
12
  if (!resolved)
12
13
  return null;
13
- return (_jsx("p", { id: id, "data-slot": "form-field-error", className: cn("text-xs text-destructive", className), role: "alert", children: resolved }));
14
+ return (_jsx("p", { id: id, "data-slot": "form-field-error", className: cn(errorText, className), role: "alert", children: resolved }));
14
15
  }
@@ -2,4 +2,4 @@
2
2
  * Calendar cell size and padding, shared with LazyCalendar's placeholder
3
3
  * (here, not in calendar.tsx, so the placeholder skips Day Picker).
4
4
  */
5
- export declare const calendarSize = "[--cell-size:--spacing(8)] [--calendar-padding:--spacing(3)]";
5
+ export declare const calendarSize = "[--cell-size:--spacing(8)] [--calendar-padding:--spacing(2)]";
@@ -2,4 +2,4 @@
2
2
  * Calendar cell size and padding, shared with LazyCalendar's placeholder
3
3
  * (here, not in calendar.tsx, so the placeholder skips Day Picker).
4
4
  */
5
- export const calendarSize = "[--cell-size:--spacing(8)] [--calendar-padding:--spacing(3)]";
5
+ export const calendarSize = "[--cell-size:--spacing(8)] [--calendar-padding:--spacing(2)]";
@@ -10,8 +10,8 @@
10
10
  export declare const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";
11
11
  /** The same ring on a wrapper with keyboard focus inside it. */
12
12
  export declare const focusRingWithin = "has-focus-visible:ring-2 has-focus-visible:ring-ring has-focus-visible:ring-offset-2 has-focus-visible:ring-offset-background";
13
- /** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
14
- export declare const invalidState = "aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40";
13
+ /** `aria-invalid`: a destructive border; keyboard focus keeps the shared solid ring. */
14
+ export declare const invalidState = "aria-invalid:border-destructive";
15
15
  /** Muted trigger text while empty (`data-placeholder` or `data-empty`). */
16
16
  export declare const emptyValueText = "data-placeholder:text-muted-foreground data-[empty=true]:text-muted-foreground";
17
17
  /** Native `disabled`: dimmed, with no hover or click. */
@@ -60,7 +60,7 @@ export declare const listboxEmpty = "px-3 py-4 text-center text-sm text-muted-fo
60
60
  export declare const listboxScroll = "max-h-60 overflow-y-auto";
61
61
  /** Options in menus and listboxes. */
62
62
  export declare const optionState: {
63
- /** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
63
+ /** Highlight as focus moves: grey fill plus a 2px ring bar, unlike checked. */
64
64
  focus: string;
65
65
  /** The same highlight for lists that track the active option in state. */
66
66
  active: string;
@@ -102,6 +102,8 @@ export declare const controlText = "text-xs font-semibold tracking-widest whites
102
102
  * DialogDescription, AlertDescription).
103
103
  */
104
104
  export declare const descriptionText = "text-sm leading-relaxed text-muted-foreground";
105
+ /** Field and form-level error text share one size, weight, and color. */
106
+ export declare const errorText = "text-sm font-normal text-destructive";
105
107
  /** Field-label text (Label, FieldLegend, FieldTitle, PopoverTitle). */
106
108
  export declare const labelText = "text-xs font-semibold tracking-wide uppercase";
107
109
  /**
@@ -10,8 +10,8 @@
10
10
  export const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";
11
11
  /** The same ring on a wrapper with keyboard focus inside it. */
12
12
  export const focusRingWithin = "has-focus-visible:ring-2 has-focus-visible:ring-ring has-focus-visible:ring-offset-2 has-focus-visible:ring-offset-background";
13
- /** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
14
- export const invalidState = "aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40";
13
+ /** `aria-invalid`: a destructive border; keyboard focus keeps the shared solid ring. */
14
+ export const invalidState = "aria-invalid:border-destructive";
15
15
  /** Muted trigger text while empty (`data-placeholder` or `data-empty`). */
16
16
  export const emptyValueText = "data-placeholder:text-muted-foreground data-[empty=true]:text-muted-foreground";
17
17
  /** Native `disabled`: dimmed, with no hover or click. */
@@ -60,7 +60,7 @@ export const listboxEmpty = "px-3 py-4 text-center text-sm text-muted-foreground
60
60
  export const listboxScroll = "max-h-60 overflow-y-auto";
61
61
  /** Options in menus and listboxes. */
62
62
  export const optionState = {
63
- /** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
63
+ /** Highlight as focus moves: grey fill plus a 2px ring bar, unlike checked. */
64
64
  focus: "focus:bg-accent focus:text-accent-foreground focus:shadow-[inset_2px_0_0_var(--color-ring)]",
65
65
  /** The same highlight for lists that track the active option in state. */
66
66
  active: "bg-accent text-accent-foreground shadow-[inset_2px_0_0_var(--color-ring)]",
@@ -102,6 +102,8 @@ export const controlText = "text-xs font-semibold tracking-widest whitespace-now
102
102
  * DialogDescription, AlertDescription).
103
103
  */
104
104
  export const descriptionText = "text-sm leading-relaxed text-muted-foreground";
105
+ /** Field and form-level error text share one size, weight, and color. */
106
+ export const errorText = "text-sm font-normal text-destructive";
105
107
  /** Field-label text (Label, FieldLegend, FieldTitle, PopoverTitle). */
106
108
  export const labelText = "text-xs font-semibold tracking-wide uppercase";
107
109
  /**
@@ -6,7 +6,7 @@ import { disabledState, focusRing, inlineIcon, selectedState, } from "./styles.j
6
6
  * without it (`auto`), the row reads the orientation from the `group/tabs`
7
7
  * root instead, as it always has for apps building their own list.
8
8
  */
9
- const tabsListVariants = cva("group/tabs-list inline-flex w-max min-w-full items-stretch justify-start p-0 text-muted-foreground", {
9
+ const tabsListVariants = cva("group/tabs-list inline-flex w-max min-w-full items-stretch justify-start p-0 after:w-1 after:shrink-0 after:content-[''] text-muted-foreground", {
10
10
  variants: {
11
11
  variant: {
12
12
  default: "bg-muted",
@@ -5,3 +5,5 @@ import * as React from "react";
5
5
  * join the tab order only while it overflows (WCAG 2.1.1).
6
6
  */
7
7
  export declare function useHorizontalOverflow(ref: React.RefObject<HTMLElement | null>): boolean;
8
+ /** Scrolls a keyboard-focused `element` and its ring into `scroller`'s view. */
9
+ export declare function revealInline(scroller: HTMLElement, element: Element): void;
@@ -21,3 +21,17 @@ export function useHorizontalOverflow(ref) {
21
21
  }, [ref]);
22
22
  return overflowing;
23
23
  }
24
+ /** Scrolls a keyboard-focused `element` and its ring into `scroller`'s view. */
25
+ export function revealInline(scroller, element) {
26
+ const rect = element.getBoundingClientRect();
27
+ const focused = scroller.ownerDocument.activeElement;
28
+ if (!rect.width || !focused?.matches(":focus-visible"))
29
+ return;
30
+ const box = scroller.getBoundingClientRect();
31
+ const before = rect.left - 8 - box.left;
32
+ const after = rect.right + 8 - box.right;
33
+ if (before < 0)
34
+ scroller.scrollLeft += before;
35
+ else if (after > 0)
36
+ scroller.scrollLeft += Math.min(after, before);
37
+ }
package/dist/kanban.js CHANGED
@@ -8,6 +8,7 @@ import { Button } from "./button.js";
8
8
  import { KANBAN_COMPLETE_ID, KANBAN_DELETE_ID, buildKanbanGroups, kanbanOverflowEdges, kanbanScrollBy, resolveKanbanDrop, } from "./internal/kanban-layout.js";
9
9
  import { configureCardSortablePlugins, configureKanbanPlugins, } from "./internal/kanban-plugins.js";
10
10
  import { focusRingWithin } from "./internal/styles.js";
11
+ import { revealInline } from "./internal/use-horizontal-overflow.js";
11
12
  import { Skeleton } from "./skeleton.js";
12
13
  import { cn } from "./utils.js";
13
14
  /** Column droppables lose to cards, so a drop lands between cards. */
@@ -77,7 +78,11 @@ function KanbanBoard({ columns, items, getColumnId, onMove, onComplete, onDelete
77
78
  }, [cardClassName, itemById, renderCard]);
78
79
  // `min-w-0` lets a flex or grid parent shrink the board so the scroller,
79
80
  // not the page, overflows.
80
- return (_jsx("div", { "data-slot": "kanban-board", className: cn("min-w-0", className), ...props, children: _jsxs(DragDropProvider, { plugins: configureKanbanPlugins, onDragOver: handleDragOver, onDragEnd: handleDragEnd, children: [_jsxs("div", { className: "relative min-w-0", children: [_jsx("div", { ref: scrollerRef, "data-slot": "kanban-scroller", "data-loading": dimmed || undefined, "data-overflow-start": overflow.start || undefined, "data-overflow-end": overflow.end || undefined, onScroll: overflow.measure, className: cn("flex min-w-0 gap-4 overflow-x-auto pb-2",
81
+ return (_jsx("div", { "data-slot": "kanban-board", className: cn("min-w-0", className), ...props, children: _jsxs(DragDropProvider, { plugins: configureKanbanPlugins, onDragOver: handleDragOver, onDragEnd: handleDragEnd, children: [_jsxs("div", { className: "relative min-w-0", children: [_jsx("div", { ref: scrollerRef, "data-slot": "kanban-scroller", "data-loading": dimmed || undefined, "data-overflow-start": overflow.start || undefined, "data-overflow-end": overflow.end || undefined, onScroll: overflow.measure, onFocus: (event) => {
82
+ const card = event.target.closest('[data-slot="kanban-card"]');
83
+ if (card)
84
+ revealInline(event.currentTarget, card);
85
+ }, className: cn("flex min-w-0 gap-4 overflow-x-auto pb-2",
81
86
  // AutoScroller scrolls in small steps near the edge; mandatory
82
87
  // snap would pull each one back, so it is off mid-drag.
83
88
  "snap-x snap-mandatory scroll-px-2 has-data-dragging:snap-none", "data-loading:opacity-60", scrollerClassName), children: columns.map((column) => {
@@ -125,7 +130,7 @@ function KanbanColumnView({ column, count, emptyLabel, loading, className, child
125
130
  collisionPriority: COLUMN_DROP_PRIORITY,
126
131
  });
127
132
  const empty = count === 0;
128
- return (_jsxs("div", { ref: ref, "data-slot": "kanban-column", "data-drop-target": isDropTarget || undefined, className: cn("flex snap-start flex-col gap-3 p-3", "bg-muted/40 ring-1 ring-foreground/5", "data-drop-target:bg-muted/60 data-drop-target:ring-2 data-drop-target:ring-ring/60", columnSize, className), children: [_jsxs("header", { "data-slot": "kanban-column-header", className: "flex flex-wrap items-start justify-between gap-x-2 gap-y-0.5", children: [_jsxs("span", { className: "flex max-w-full min-w-0 items-center gap-2 text-xs font-semibold tracking-widest uppercase", children: [column.color ? (_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-full", style: { backgroundColor: column.color } })) : null, _jsx("span", { className: "truncate leading-tight", title: column.label, children: column.label })] }), _jsxs("span", { className: "ml-auto flex max-w-full min-w-0 flex-col items-end gap-0.5 text-muted-foreground tabular-nums", children: [_jsx("span", { className: "text-xs", children: count }), column.badge ? (_jsx("span", { className: "max-w-full truncate text-[10px] font-medium", children: column.badge })) : null] })] }), _jsx("div", { className: "-m-1 mr-0 flex min-h-32 flex-1 flex-col gap-2 overflow-y-auto p-1", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20" }), _jsx(Skeleton, { className: "h-16" })] })) : (_jsxs(_Fragment, { children: [children, _jsx("p", { "data-slot": "kanban-column-empty", "aria-hidden": !empty, className: cn("border border-dashed px-3 py-6 text-center text-xs text-muted-foreground", !empty && "hidden"), children: emptyLabel })] })) })] }));
133
+ return (_jsxs("div", { ref: ref, "data-slot": "kanban-column", "data-drop-target": isDropTarget || undefined, className: cn("flex snap-start flex-col gap-3 p-3", "bg-muted/40 ring-1 ring-foreground/5", "data-drop-target:bg-muted/60 data-drop-target:ring-2 data-drop-target:ring-ring/60", columnSize, className), children: [_jsxs("div", { "data-slot": "kanban-column-header", className: "flex flex-wrap items-start justify-between gap-x-2 gap-y-0.5", children: [_jsxs("span", { className: "flex max-w-full min-w-0 items-center gap-2 text-xs font-semibold tracking-widest uppercase", children: [column.color ? (_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-full", style: { backgroundColor: column.color } })) : null, _jsx("span", { className: "truncate leading-tight", title: column.label, children: column.label })] }), _jsxs("span", { className: "ml-auto flex max-w-full min-w-0 flex-col items-end gap-0.5 text-muted-foreground tabular-nums", children: [_jsx("span", { className: "text-xs", children: count }), column.badge ? (_jsx("span", { className: "max-w-full truncate text-[10px] font-medium", children: column.badge })) : null] })] }), _jsx("div", { className: "-m-1 mr-0 flex min-h-32 flex-1 flex-col gap-2 overflow-y-auto p-1", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20" }), _jsx(Skeleton, { className: "h-16" })] })) : (_jsxs(_Fragment, { children: [children, _jsx("p", { "data-slot": "kanban-column-empty", "aria-hidden": !empty, className: cn("border border-dashed px-3 py-6 text-center text-xs text-muted-foreground", !empty && "hidden"), children: emptyLabel })] })) })] }));
129
134
  }
130
135
  const cardChrome = cn("flex border border-border bg-card text-card-foreground");
131
136
  const handleChrome = cn("flex shrink-0 items-center self-stretch border-r border-border", "bg-muted/30 px-1.5 text-muted-foreground");
package/dist/meter.js CHANGED
@@ -15,6 +15,6 @@ function Meter({ value, min = 0, max, fillClassName = "bg-primary", markers, "ar
15
15
  return 0;
16
16
  return clamp(((amount - min) / span) * 100, 0, 100);
17
17
  };
18
- return (_jsxs("div", { ...props, role: "meter", "data-slot": "meter", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": clamp(current, min, max), "aria-label": ariaLabel, className: cn("relative h-1.5 w-full overflow-hidden bg-muted", className), children: [_jsx("div", { "data-slot": "meter-fill", className: cn("h-full", fillClassName), style: { width: `${toPercent(current)}%` } }), markers?.map((marker) => (_jsx("div", { "aria-hidden": true, "data-slot": "meter-marker", className: cn("absolute inset-y-0 w-px bg-foreground/40", marker.className), style: { left: `${toPercent(marker.position)}%` } }, marker.position)))] }));
18
+ return (_jsxs("div", { ...props, role: "meter", "data-slot": "meter", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": clamp(current, min, max), "aria-label": ariaLabel, className: cn("relative h-1.5 w-full overflow-hidden bg-input", className), children: [_jsx("div", { "data-slot": "meter-fill", className: cn("h-full", fillClassName), style: { width: `${toPercent(current)}%` } }), markers?.map((marker) => (_jsx("div", { "aria-hidden": true, "data-slot": "meter-marker", className: cn("absolute inset-y-0 w-px bg-foreground/40", marker.className), style: { left: `${toPercent(marker.position)}%` } }, marker.position)))] }));
19
19
  }
20
20
  export { Meter };
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  /**
3
3
  * Page links for long lists and tables. Links are plain anchors so the page
4
4
  * lives in the URL; pass `onClick` with `href` for client-side routing.
5
- * The nav is a plain flex row: place it with `className` (`justify-center`,
5
+ * The nav fills its container: place its links with `className` (`justify-center`,
6
6
  * `justify-end`, `mx-auto`) to suit the layout.
7
7
  */
8
8
  declare function Pagination({ className, ...props }: React.ComponentProps<"nav">): React.JSX.Element;
@@ -7,14 +7,14 @@ import { cn } from "./utils.js";
7
7
  /**
8
8
  * Page links for long lists and tables. Links are plain anchors so the page
9
9
  * lives in the URL; pass `onClick` with `href` for client-side routing.
10
- * The nav is a plain flex row: place it with `className` (`justify-center`,
10
+ * The nav fills its container: place its links with `className` (`justify-center`,
11
11
  * `justify-end`, `mx-auto`) to suit the layout.
12
12
  */
13
13
  function Pagination({ className, ...props }) {
14
- return (_jsx("nav", { "aria-label": "Pagination", "data-slot": "pagination", className: cn("flex", className), ...props }));
14
+ return (_jsx("nav", { "aria-label": "Pagination", "data-slot": "pagination", className: cn("@container/pagination flex w-full min-w-0", className), ...props }));
15
15
  }
16
16
  function PaginationContent({ className, ...props }) {
17
- return (_jsx("ul", { "data-slot": "pagination-content", className: cn("flex flex-row items-center gap-1", className), ...props }));
17
+ return (_jsx("ul", { "data-slot": "pagination-content", className: cn("flex flex-row flex-wrap items-center gap-1", className), ...props }));
18
18
  }
19
19
  function PaginationItem({ ...props }) {
20
20
  return _jsx("li", { "data-slot": "pagination-item", ...props });
package/dist/progress.js CHANGED
@@ -14,6 +14,6 @@ function Progress({ className, indicatorClassName, value, max = 100, ...props })
14
14
  const scale = Number.isFinite(max) && max > 0 ? max : 100;
15
15
  const current = value != null && Number.isFinite(value) ? clamp(value, 0, scale) : null;
16
16
  const filled = current == null ? 0 : (current / scale) * 100;
17
- return (_jsx(ProgressPrimitive.Root, { "data-slot": "progress", value: current, max: scale, className: cn("relative h-2 w-full overflow-hidden rounded-none bg-primary/15", className), ...props, children: _jsx(ProgressPrimitive.Indicator, { "data-slot": "progress-indicator", className: cn("h-full w-full flex-1 bg-primary transition-transform", indicatorClassName), style: { transform: `translateX(-${100 - filled}%)` } }) }));
17
+ return (_jsx(ProgressPrimitive.Root, { "data-slot": "progress", value: current, max: scale, className: cn("relative h-2 w-full overflow-hidden rounded-none bg-input", className), ...props, children: _jsx(ProgressPrimitive.Indicator, { "data-slot": "progress-indicator", className: cn("h-full w-full flex-1 bg-primary transition-transform", indicatorClassName), style: { transform: `translateX(-${100 - filled}%)` } }) }));
18
18
  }
19
19
  export { Progress };
package/dist/separator.js CHANGED
@@ -8,6 +8,6 @@ import { cn } from "./utils.js";
8
8
  * `orientation`, so `className="w-12"` or `"w-0.5"` replaces them.
9
9
  */
10
10
  function Separator({ className, orientation = "horizontal", decorative = true, ...props }) {
11
- return (_jsx(SeparatorPrimitive.Root, { "data-slot": "separator", decorative: decorative, orientation: orientation, className: cn("shrink-0 bg-border", orientation === "vertical" ? "w-px self-stretch" : "h-px w-full", className), ...props }));
11
+ return (_jsx(SeparatorPrimitive.Root, { "data-slot": "separator", decorative: decorative, orientation: orientation, className: cn("shrink-0 bg-input", orientation === "vertical" ? "w-px self-stretch" : "h-px w-full", className), ...props }));
12
12
  }
13
13
  export { Separator };
package/dist/skeleton.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "./utils.js";
3
3
  function Skeleton({ className, ...props }) {
4
- return (_jsx("div", { "data-slot": "skeleton", className: cn("animate-pulse bg-muted", className), ...props }));
4
+ return (_jsx("div", { "data-slot": "skeleton", className: cn("animate-pulse bg-input", className), ...props }));
5
5
  }
6
6
  export { Skeleton };
package/dist/slider.js CHANGED
@@ -18,6 +18,6 @@ function Slider({ value, onValueChange, onValueCommit, min = 0, max = 100, step
18
18
  const amount = next[0];
19
19
  if (amount !== undefined)
20
20
  onValueCommit?.(amount);
21
- }, className: cn(dataDisabledState, "relative flex w-full touch-none items-center select-none", className), children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative h-1.5 w-full grow overflow-hidden rounded-none bg-muted", children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: "absolute h-full bg-primary" }) }), _jsx(SliderPrimitive.Thumb, { id: id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-slot": "slider-thumb", className: cn(focusRing, hitArea44, "relative block size-4 shrink-0 rounded-none border border-primary bg-background shadow-xs") })] }));
21
+ }, className: cn(dataDisabledState, "relative flex w-full touch-none items-center select-none", className), children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative h-1.5 w-full grow overflow-hidden rounded-none bg-input", children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: "absolute h-full bg-primary" }) }), _jsx(SliderPrimitive.Thumb, { id: id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-slot": "slider-thumb", className: cn(focusRing, hitArea44, "relative block size-4 shrink-0 rounded-none border border-primary bg-background shadow-xs") })] }));
22
22
  }
23
23
  export { Slider };
package/dist/tabs.js CHANGED
@@ -7,7 +7,7 @@ import { activeLinkAttributes, activeLinkSelector, } from "./internal/active-lin
7
7
  import { focusRing } from "./internal/styles.js";
8
8
  import { tabsListVariants, tabsScrollerVariants, tabsTriggerVariants, } from "./internal/tabs-styles.js";
9
9
  import { useDragScroll } from "./internal/use-drag-scroll.js";
10
- import { useHorizontalOverflow } from "./internal/use-horizontal-overflow.js";
10
+ import { revealInline, useHorizontalOverflow, } from "./internal/use-horizontal-overflow.js";
11
11
  import { cn } from "./utils.js";
12
12
  /**
13
13
  * The root's orientation, so the list can pick plain size classes (`h-7`)
@@ -48,7 +48,10 @@ function TabsScroller({ variant = "default", orientation = "horizontal", classNa
48
48
  behavior: "auto",
49
49
  });
50
50
  });
51
- return (_jsx("div", { ref: ref, "data-slot": "tabs-scroller", "data-variant": variant, tabIndex: overflowing ? 0 : undefined, className: cn(focusRing, tabsScrollerVariants({ variant, orientation }), className), children: children }));
51
+ return (_jsx("div", { ref: ref, onFocus: (event) => {
52
+ if (event.target !== event.currentTarget)
53
+ revealInline(event.currentTarget, event.target);
54
+ }, "data-slot": "tabs-scroller", "data-variant": variant, tabIndex: overflowing ? 0 : undefined, className: cn(focusRing, tabsScrollerVariants({ variant, orientation }), className), children: children }));
52
55
  }
53
56
  /**
54
57
  * The tab row inside its scroller. `className` styles the list (its `h-7`
package/dist/toast.js CHANGED
@@ -5,7 +5,7 @@ import { CircleAlertIcon, CircleCheckIcon, InfoIcon, LoaderCircleIcon, XIcon, }
5
5
  import { useTheme } from "next-themes";
6
6
  import { Toaster as Sonner, toast as sonnerToast, } from "sonner";
7
7
  import { buttonVariants } from "./button.js";
8
- import { statusTone } from "./internal/styles.js";
8
+ import { focusRing, statusTone } from "./internal/styles.js";
9
9
  import { cn } from "./utils.js";
10
10
  /**
11
11
  * Copies only the kit's options, so plain JavaScript callers cannot hand the
@@ -45,27 +45,18 @@ const toast = Object.assign(show(sonnerToast), {
45
45
  * `error`, and `loading` each show a status icon. Tone colors the icon, title,
46
46
  * and border (`statusTone`, shared with Alert), not the body text.
47
47
  *
48
- * Close button: a circular badge centred on the top-right corner (`-top-2
49
- * -right-2`, half outside the border). Ghost / icon-xs still supplies the
50
- * shared focus ring, but Sonner's unlayered `[data-button]` default
51
- * (`background: var(--normal-text)`) and the dark `[data-close-button]` rule
52
- * (not gated on `data-styled`) would paint a foreground square. Important
53
- * popover fill, border, and muted text beat those fills. The 24px hit area
54
- * is the badge itself; the X is 12px. `overflow-visible` on the toast and
55
- * toaster keeps stacked badges from being clipped.
48
+ * Close button: a 24px square inside the top-right corner. Important kit
49
+ * utilities beat Sonner's unlayered close-button positioning and colors.
56
50
  *
57
- * Stacking: while the stack is collapsed, every part of the toasts behind
58
- * the front one is `invisible`. Their badges would otherwise peek out above
59
- * the front toast, closer together than the 24px target spacing, and their
60
- * buttons would stay in the tab order unseen. Hovering or focusing the stack
61
- * expands it (see `Toaster`), which shows them again.
51
+ * Stacking: the toaster floats at `position`; collapsed back toasts are
52
+ * `invisible`. Hovering or focusing expands it (see `Toaster`).
62
53
  *
63
- * Padding is asymmetric: `pl-4` matches Alert; `pr-6` clears the badge
64
- * (about 16px of it sits inside the border). Icon-to-text is `gap-x-2.5`
54
+ * Padding is asymmetric: `pl-4` matches Alert; `pr-10` clears the close
55
+ * button. Icon-to-text is `gap-x-2.5`
65
56
  * so the status icon and copy read as one unit.
66
57
  */
67
58
  const classNames = {
68
- toast: "relative flex w-max max-w-[min(356px,calc(100vw-2rem))] flex-wrap items-center justify-end gap-x-2.5 gap-y-2 overflow-visible rounded-none border border-border bg-popover py-3 pl-4 pr-6 text-sm text-popover-foreground shadow-md data-[expanded=false]:data-[front=false]:*:invisible",
59
+ toast: cn(focusRing, "relative flex w-max max-w-[min(356px,calc(100vw-2rem))] flex-wrap items-center justify-end gap-x-2.5 gap-y-2 overflow-visible rounded-none border border-border bg-popover py-3 pl-4 pr-10 text-sm text-popover-foreground shadow-md focus-visible:!shadow-[var(--tw-ring-offset-shadow),var(--tw-ring-shadow)] data-[visible=false]:invisible data-[expanded=false]:data-[front=false]:*:invisible"),
69
60
  // Fills the row beside the icon, so action buttons wrap to their own row
70
61
  // (aligned right by the toast's justify-end). Without an icon the basis
71
62
  // falls back to the title's width, so a one-word toast stays compact.
@@ -76,7 +67,7 @@ const classNames = {
76
67
  icon: "flex size-4 shrink-0 items-center justify-center [&_svg]:size-4",
77
68
  actionButton: cn(buttonVariants({ size: "xs" }), "shrink-0"),
78
69
  cancelButton: cn(buttonVariants({ variant: "outline", size: "xs" }), "shrink-0"),
79
- closeButton: cn(buttonVariants({ variant: "ghost", size: "icon-xs" }), "!absolute !-top-2 !-right-2 !left-auto !z-10 !size-6 !rounded-full !border !border-border !bg-popover !p-0 !text-muted-foreground !shadow-xs !translate-x-0 !translate-y-0 hover:!border-border hover:!bg-popover hover:!text-foreground focus-visible:!text-foreground active:!translate-y-0 [&_svg]:!size-3"),
70
+ closeButton: cn(buttonVariants({ variant: "ghost", size: "icon-xs" }), "!absolute !top-2 !right-2 !left-auto !z-10 !size-6 !rounded-none !border !border-border !bg-popover !p-0 !text-muted-foreground !translate-x-0 !translate-y-0 hover:!border-border hover:!bg-popover hover:!text-foreground focus-visible:!text-foreground active:!translate-y-0 [&_svg]:!size-3"),
80
71
  success: statusTone.success,
81
72
  info: statusTone.neutral,
82
73
  warning: statusTone.warning,
@@ -104,6 +95,6 @@ const Toaster = ({ position, closeButton = true, expand, visibleToasts, duration
104
95
  setFocusWithin(false);
105
96
  }
106
97
  };
107
- return (_jsx("div", { "data-slot": "toaster", className: "contents", onFocus: () => setFocusWithin(true), onBlur: onBlur, children: _jsx(Sonner, { theme: theme, className: "toaster group overflow-visible", position: position, closeButton: closeButton, expand: expand || focusWithin, visibleToasts: visibleToasts, duration: duration, icons: icons, toastOptions: { unstyled: true, classNames } }) }));
98
+ return (_jsx("div", { "data-slot": "toaster", className: "contents", onFocus: () => setFocusWithin(true), onBlur: onBlur, children: _jsx(Sonner, { theme: theme, className: "toaster group overflow-visible", style: { fontFamily: "var(--font-sans)" }, position: position, closeButton: closeButton, expand: expand || focusWithin, visibleToasts: visibleToasts, duration: duration, icons: icons, toastOptions: { unstyled: true, classNames } }) }));
108
99
  };
109
100
  export { Toaster, toast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atlas-react-kit",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Shared React components and Tailwind theme for Atlas",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/theme.css CHANGED
@@ -227,6 +227,305 @@
227
227
  * without `!`. Size and flush still set the variable on `[data-slot=card]`.
228
228
  */
229
229
  @layer components {
230
+ /*
231
+ * Pagination that no longer fits its container drops pages in two steps,
232
+ * sized for 36px page links, 38px icon-only Previous / Next and a 4px gap
233
+ * (40px per item; `n` items fit in n x 40px). The first and last <li> are
234
+ * Previous and Next.
235
+ * 1. A list with an ellipsis keeps Previous, the first and last pages, the
236
+ * current page, the ellipses and Next (plus a lone page between the
237
+ * first or last page and the current one), so every gap is still marked.
238
+ * 2. Narrower still, or for a list without an ellipsis, only Previous, the
239
+ * current page and Next stay. The list also wraps rather than spill.
240
+ */
241
+ @container pagination (width < 10rem) {
242
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
243
+ [data-slot="pagination-ellipsis"]
244
+ ):has(> li:nth-child(4)):not(:has(> li:nth-child(5)))
245
+ > li:not(
246
+ :first-child,
247
+ :last-child,
248
+ :nth-child(2),
249
+ :nth-last-child(2),
250
+ :has([aria-current="page"]),
251
+ :has([data-slot="pagination-ellipsis"]),
252
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
253
+ + li [aria-current="page"]
254
+ ),
255
+ :has([aria-current="page"])
256
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
257
+ ),
258
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
259
+ :has([data-slot="pagination-ellipsis"])
260
+ ):has(> li:nth-child(4)):not(:has(> li:nth-child(5)))
261
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
262
+ display: none;
263
+ }
264
+ }
265
+ @container pagination (width < 7.5rem) {
266
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
267
+ [data-slot="pagination-ellipsis"]
268
+ ):has(> li:nth-child(4)):not(:has(> li:nth-child(5)))
269
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
270
+ display: none;
271
+ }
272
+ }
273
+ @container pagination (width < 12.5rem) {
274
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
275
+ [data-slot="pagination-ellipsis"]
276
+ ):has(> li:nth-child(5)):not(:has(> li:nth-child(6)))
277
+ > li:not(
278
+ :first-child,
279
+ :last-child,
280
+ :nth-child(2),
281
+ :nth-last-child(2),
282
+ :has([aria-current="page"]),
283
+ :has([data-slot="pagination-ellipsis"]),
284
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
285
+ + li [aria-current="page"]
286
+ ),
287
+ :has([aria-current="page"])
288
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
289
+ ),
290
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
291
+ :has([data-slot="pagination-ellipsis"])
292
+ ):has(> li:nth-child(5)):not(:has(> li:nth-child(6)))
293
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
294
+ display: none;
295
+ }
296
+ }
297
+ @container pagination (width < 10rem) {
298
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
299
+ [data-slot="pagination-ellipsis"]
300
+ ):has(> li:nth-child(5)):not(:has(> li:nth-child(6)))
301
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
302
+ display: none;
303
+ }
304
+ }
305
+ @container pagination (width < 15rem) {
306
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
307
+ [data-slot="pagination-ellipsis"]
308
+ ):has(> li:nth-child(6)):not(:has(> li:nth-child(7)))
309
+ > li:not(
310
+ :first-child,
311
+ :last-child,
312
+ :nth-child(2),
313
+ :nth-last-child(2),
314
+ :has([aria-current="page"]),
315
+ :has([data-slot="pagination-ellipsis"]),
316
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
317
+ + li [aria-current="page"]
318
+ ),
319
+ :has([aria-current="page"])
320
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
321
+ ),
322
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
323
+ :has([data-slot="pagination-ellipsis"])
324
+ ):has(> li:nth-child(6)):not(:has(> li:nth-child(7)))
325
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
326
+ display: none;
327
+ }
328
+ }
329
+ @container pagination (width < 12.5rem) {
330
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
331
+ [data-slot="pagination-ellipsis"]
332
+ ):has(> li:nth-child(6)):not(:has(> li:nth-child(7)))
333
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
334
+ display: none;
335
+ }
336
+ }
337
+ @container pagination (width < 17.5rem) {
338
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
339
+ [data-slot="pagination-ellipsis"]
340
+ ):has(> li:nth-child(7)):not(:has(> li:nth-child(8)))
341
+ > li:not(
342
+ :first-child,
343
+ :last-child,
344
+ :nth-child(2),
345
+ :nth-last-child(2),
346
+ :has([aria-current="page"]),
347
+ :has([data-slot="pagination-ellipsis"]),
348
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
349
+ + li [aria-current="page"]
350
+ ),
351
+ :has([aria-current="page"])
352
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
353
+ ),
354
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
355
+ :has([data-slot="pagination-ellipsis"])
356
+ ):has(> li:nth-child(7)):not(:has(> li:nth-child(8)))
357
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
358
+ display: none;
359
+ }
360
+ }
361
+ @container pagination (width < 15rem) {
362
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
363
+ [data-slot="pagination-ellipsis"]
364
+ ):has(> li:nth-child(7)):not(:has(> li:nth-child(8)))
365
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
366
+ display: none;
367
+ }
368
+ }
369
+ @container pagination (width < 20rem) {
370
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
371
+ [data-slot="pagination-ellipsis"]
372
+ ):has(> li:nth-child(8)):not(:has(> li:nth-child(9)))
373
+ > li:not(
374
+ :first-child,
375
+ :last-child,
376
+ :nth-child(2),
377
+ :nth-last-child(2),
378
+ :has([aria-current="page"]),
379
+ :has([data-slot="pagination-ellipsis"]),
380
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
381
+ + li [aria-current="page"]
382
+ ),
383
+ :has([aria-current="page"])
384
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
385
+ ),
386
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
387
+ :has([data-slot="pagination-ellipsis"])
388
+ ):has(> li:nth-child(8)):not(:has(> li:nth-child(9)))
389
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
390
+ display: none;
391
+ }
392
+ }
393
+ @container pagination (width < 17.5rem) {
394
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
395
+ [data-slot="pagination-ellipsis"]
396
+ ):has(> li:nth-child(8)):not(:has(> li:nth-child(9)))
397
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
398
+ display: none;
399
+ }
400
+ }
401
+ @container pagination (width < 22.5rem) {
402
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
403
+ [data-slot="pagination-ellipsis"]
404
+ ):has(> li:nth-child(9)):not(:has(> li:nth-child(10)))
405
+ > li:not(
406
+ :first-child,
407
+ :last-child,
408
+ :nth-child(2),
409
+ :nth-last-child(2),
410
+ :has([aria-current="page"]),
411
+ :has([data-slot="pagination-ellipsis"]),
412
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
413
+ + li [aria-current="page"]
414
+ ),
415
+ :has([aria-current="page"])
416
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
417
+ ),
418
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
419
+ :has([data-slot="pagination-ellipsis"])
420
+ ):has(> li:nth-child(9)):not(:has(> li:nth-child(10)))
421
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
422
+ display: none;
423
+ }
424
+ }
425
+ @container pagination (width < 17.5rem) {
426
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
427
+ [data-slot="pagination-ellipsis"]
428
+ ):has(> li:nth-child(9)):not(:has(> li:nth-child(10)))
429
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
430
+ display: none;
431
+ }
432
+ }
433
+ @container pagination (width < 25rem) {
434
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
435
+ [data-slot="pagination-ellipsis"]
436
+ ):has(> li:nth-child(10)):not(:has(> li:nth-child(11)))
437
+ > li:not(
438
+ :first-child,
439
+ :last-child,
440
+ :nth-child(2),
441
+ :nth-last-child(2),
442
+ :has([aria-current="page"]),
443
+ :has([data-slot="pagination-ellipsis"]),
444
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
445
+ + li [aria-current="page"]
446
+ ),
447
+ :has([aria-current="page"])
448
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
449
+ ),
450
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
451
+ :has([data-slot="pagination-ellipsis"])
452
+ ):has(> li:nth-child(10)):not(:has(> li:nth-child(11)))
453
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
454
+ display: none;
455
+ }
456
+ }
457
+ @container pagination (width < 17.5rem) {
458
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
459
+ [data-slot="pagination-ellipsis"]
460
+ ):has(> li:nth-child(10)):not(:has(> li:nth-child(11)))
461
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
462
+ display: none;
463
+ }
464
+ }
465
+ @container pagination (width < 27.5rem) {
466
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
467
+ [data-slot="pagination-ellipsis"]
468
+ ):has(> li:nth-child(11)):not(:has(> li:nth-child(12)))
469
+ > li:not(
470
+ :first-child,
471
+ :last-child,
472
+ :nth-child(2),
473
+ :nth-last-child(2),
474
+ :has([aria-current="page"]),
475
+ :has([data-slot="pagination-ellipsis"]),
476
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
477
+ + li [aria-current="page"]
478
+ ),
479
+ :has([aria-current="page"])
480
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
481
+ ),
482
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
483
+ :has([data-slot="pagination-ellipsis"])
484
+ ):has(> li:nth-child(11)):not(:has(> li:nth-child(12)))
485
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
486
+ display: none;
487
+ }
488
+ }
489
+ @container pagination (width < 17.5rem) {
490
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
491
+ [data-slot="pagination-ellipsis"]
492
+ ):has(> li:nth-child(11)):not(:has(> li:nth-child(12)))
493
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
494
+ display: none;
495
+ }
496
+ }
497
+ @container pagination (width < 30rem) {
498
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
499
+ [data-slot="pagination-ellipsis"]
500
+ ):has(> li:nth-child(12))
501
+ > li:not(
502
+ :first-child,
503
+ :last-child,
504
+ :nth-child(2),
505
+ :nth-last-child(2),
506
+ :has([aria-current="page"]),
507
+ :has([data-slot="pagination-ellipsis"]),
508
+ :nth-child(3):not(:has([data-slot="pagination-ellipsis"]) + li):has(
509
+ + li [aria-current="page"]
510
+ ),
511
+ :has([aria-current="page"])
512
+ + :nth-last-child(3):not(:has(+ li [data-slot="pagination-ellipsis"]))
513
+ ),
514
+ [data-slot="pagination-content"]:has([aria-current="page"]):not(
515
+ :has([data-slot="pagination-ellipsis"])
516
+ ):has(> li:nth-child(12))
517
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
518
+ display: none;
519
+ }
520
+ }
521
+ @container pagination (width < 17.5rem) {
522
+ [data-slot="pagination-content"]:has([aria-current="page"]):has(
523
+ [data-slot="pagination-ellipsis"]
524
+ ):has(> li:nth-child(12))
525
+ > li:not(:first-child, :last-child, :has([aria-current="page"])) {
526
+ display: none;
527
+ }
528
+ }
230
529
  [data-slot="card"] {
231
530
  padding-block: var(--card-p);
232
531
  }