@apliteni/apliteni-ui 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/react/README.md +47 -1
- package/react/dist/index.d.ts +69 -2
- package/react/dist/index.js +126 -0
- package/src/components/index.js +7 -5
- package/src/components/success.js +30 -37
- package/src/index.d.ts +1 -1
- package/src/styles/callout.css +7 -1
- package/src/styles/success.css +33 -81
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -29,7 +29,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
|
|
|
29
29
|
import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Components: `Checkbox`, `Switch`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
|
|
32
|
+
Components: `Success`, `SuccessPanel`, `SuccessCheck`, `Checkbox`, `Switch`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
|
|
33
33
|
|
|
34
34
|
`Pill` renders metadata with the existing pill spacing. Omit `variant` for neutral
|
|
35
35
|
metadata or use `live` or `soon`. Its children accept React content.
|
|
@@ -553,6 +553,52 @@ Tooltip accepts text and inline children, without nested controls. Hovering or f
|
|
|
553
553
|
<Tooltip text="Updated daily">Balance</Tooltip>
|
|
554
554
|
```
|
|
555
555
|
|
|
556
|
+
## Success
|
|
557
|
+
|
|
558
|
+
`SuccessPanel` confirms an outcome inside the current page with `title` and optional
|
|
559
|
+
`sub`, takes the same `check` as `Success`, and forwards a ref to its root div. `SuccessCheck` is the bare mark both
|
|
560
|
+
confirmations draw, matching the kit's `successCheck()`, and forwards a ref to its
|
|
561
|
+
`svg`. It carries no size or box of its own: to compose it alone, put it in a wrapper
|
|
562
|
+
that sets both, such as the kit's `ui-success__check`, and name the outcome in text
|
|
563
|
+
next to it.
|
|
564
|
+
|
|
565
|
+
```tsx
|
|
566
|
+
<div className="ui-success__check"><SuccessCheck /></div>
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
The mark comes in two: `line` (the default) is the bare Lucide check in the success
|
|
570
|
+
colour, which strokes itself on and takes its size from the layout it lands in;
|
|
571
|
+
`circled` is Lucide `circle-check-big` at 20px, the kit's label size, drawn at rest and
|
|
572
|
+
the same size everywhere — in every `Success` layout and in `SuccessPanel`. Pick it with `check` on `Success` or `variant` on
|
|
573
|
+
`SuccessCheck`. Guidelines / Iconography reserves a circled glyph for a state and a
|
|
574
|
+
bare one for an action, and a confirmation reports a state — so `circled` is the mark
|
|
575
|
+
that rule asks for, and `line` is the default because it carries the moment at page
|
|
576
|
+
size. Neither has a filled disc behind it.
|
|
577
|
+
|
|
578
|
+
`Success` provides `hero`, `split`, and `compact` layouts on a plain elevated card;
|
|
579
|
+
there is no backdrop layer and no `backdrop` prop. Pass `title`, one short `body`
|
|
580
|
+
line, and React `actions`. There is no `eyebrow` prop: a confirmation carries one
|
|
581
|
+
title and at most one line under it, so put the outcome in the title rather than in a
|
|
582
|
+
label above it. A kit `Button` carries the focus ring already; a router link or
|
|
583
|
+
a plain `<a>` must also take `className="ui-focusable"`, or it falls back to the
|
|
584
|
+
browser's own focus outline. An omitted or empty `actions`
|
|
585
|
+
leaves out the actions row. Hero and split default to h1; compact defaults to h2.
|
|
586
|
+
`level` overrides the heading rank, and a value outside 1–6 falls back to that layout
|
|
587
|
+
default. `confetti` enables the existing decorative animation. All three components
|
|
588
|
+
use the kit CSS and its reduced-motion rules.
|
|
589
|
+
|
|
590
|
+
```tsx
|
|
591
|
+
<Success title="Changes saved" check="circled"
|
|
592
|
+
countdown={waiting ? { seconds: 5, label: 'Continuing' } : null}
|
|
593
|
+
onCountdownEnd={continueToNextPage}
|
|
594
|
+
actions={<Button onClick={() => setWaiting(false)}>Stay here</Button>} />
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Removing `countdown` or unmounting cancels the timer. Changing its duration restarts
|
|
598
|
+
it; changing its label or callback does not. Durations are whole seconds rounded
|
|
599
|
+
down, with five seconds used for omitted, non-finite, or sub-one values. Completion
|
|
600
|
+
fires once per countdown, including in StrictMode. Navigation belongs to the caller.
|
|
601
|
+
|
|
556
602
|
## Checkbox and Switch
|
|
557
603
|
|
|
558
604
|
`Checkbox` wraps a native checkbox and its visible `label`. Set `type="radio"`
|
package/react/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { InputHTMLAttributes, ReactNode, ButtonHTMLAttributes, RefAttributes, ReactElement, AnchorHTMLAttributes, HTMLAttributes, RefObject, MouseEvent, ElementType, ComponentPropsWithoutRef, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import { InputHTMLAttributes, ReactNode, ButtonHTMLAttributes, RefAttributes, ReactElement, AnchorHTMLAttributes, HTMLAttributes, RefObject, MouseEvent, ElementType, ComponentPropsWithoutRef, SelectHTMLAttributes, TextareaHTMLAttributes, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
declare function Icon({ name }: {
|
|
5
5
|
name: string;
|
|
@@ -679,6 +679,73 @@ type TooltipProps = {
|
|
|
679
679
|
};
|
|
680
680
|
declare function Tooltip({ text, children }: TooltipProps): react.JSX.Element;
|
|
681
681
|
|
|
682
|
+
type SuccessMark = 'line' | 'circled';
|
|
683
|
+
type SuccessCheckProps = Omit<SVGAttributes<SVGSVGElement>, 'children'> & {
|
|
684
|
+
/** `line` is the bare Lucide check and draws itself on; `circled` is Lucide
|
|
685
|
+
* circle-check-big at 20px, the kit's label size, drawn at rest. */
|
|
686
|
+
variant?: SuccessMark;
|
|
687
|
+
};
|
|
688
|
+
/** Decorative mark. Supply the outcome in adjacent text. */
|
|
689
|
+
declare const SuccessCheck: react.ForwardRefExoticComponent<Omit<SVGAttributes<SVGSVGElement>, "children"> & {
|
|
690
|
+
/** `line` is the bare Lucide check and draws itself on; `circled` is Lucide
|
|
691
|
+
* circle-check-big at 20px, the kit's label size, drawn at rest. */
|
|
692
|
+
variant?: SuccessMark;
|
|
693
|
+
} & react.RefAttributes<SVGSVGElement>>;
|
|
694
|
+
type SuccessPanelProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'> & {
|
|
695
|
+
title?: string;
|
|
696
|
+
sub?: string;
|
|
697
|
+
/** Which check mark to draw. See SuccessCheck. */
|
|
698
|
+
check?: SuccessMark;
|
|
699
|
+
};
|
|
700
|
+
declare const SuccessPanel: react.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> & {
|
|
701
|
+
title?: string;
|
|
702
|
+
sub?: string;
|
|
703
|
+
/** Which check mark to draw. See SuccessCheck. */
|
|
704
|
+
check?: SuccessMark;
|
|
705
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
706
|
+
type SuccessCountdown = {
|
|
707
|
+
seconds?: number;
|
|
708
|
+
label?: string;
|
|
709
|
+
};
|
|
710
|
+
type SuccessProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'> & {
|
|
711
|
+
layout?: 'hero' | 'split' | 'compact';
|
|
712
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
713
|
+
/** Which check mark to draw. See SuccessCheck. */
|
|
714
|
+
check?: SuccessMark;
|
|
715
|
+
title?: string;
|
|
716
|
+
/** One short line under the title, or nothing. There is no eyebrow tier.
|
|
717
|
+
* why: docs/specification.md#success-confirmations */
|
|
718
|
+
body?: string;
|
|
719
|
+
/** Removed: a confirmation carries one title and at most one line. Typed as
|
|
720
|
+
* `never` so a caller still passing it is told where it went rather than
|
|
721
|
+
* having it spread silently onto the root as an unknown DOM attribute. */
|
|
722
|
+
eyebrow?: never;
|
|
723
|
+
actions?: ReactNode;
|
|
724
|
+
confetti?: boolean;
|
|
725
|
+
/** Set to null to cancel; changing seconds starts a new countdown. */
|
|
726
|
+
countdown?: SuccessCountdown | null;
|
|
727
|
+
onCountdownEnd?: () => void;
|
|
728
|
+
};
|
|
729
|
+
declare const Success: react.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> & {
|
|
730
|
+
layout?: "hero" | "split" | "compact";
|
|
731
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
732
|
+
/** Which check mark to draw. See SuccessCheck. */
|
|
733
|
+
check?: SuccessMark;
|
|
734
|
+
title?: string;
|
|
735
|
+
/** One short line under the title, or nothing. There is no eyebrow tier.
|
|
736
|
+
* why: docs/specification.md#success-confirmations */
|
|
737
|
+
body?: string;
|
|
738
|
+
/** Removed: a confirmation carries one title and at most one line. Typed as
|
|
739
|
+
* `never` so a caller still passing it is told where it went rather than
|
|
740
|
+
* having it spread silently onto the root as an unknown DOM attribute. */
|
|
741
|
+
eyebrow?: never;
|
|
742
|
+
actions?: ReactNode;
|
|
743
|
+
confetti?: boolean;
|
|
744
|
+
/** Set to null to cancel; changing seconds starts a new countdown. */
|
|
745
|
+
countdown?: SuccessCountdown | null;
|
|
746
|
+
onCountdownEnd?: () => void;
|
|
747
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
748
|
+
|
|
682
749
|
type SidebarNavBadge = string | number | {
|
|
683
750
|
text: string | number;
|
|
684
751
|
tone?: 'neutral' | 'accent' | 'live' | 'danger';
|
|
@@ -732,4 +799,4 @@ declare const SidebarNav: react.ForwardRefExoticComponent<Omit<Omit<react.Detail
|
|
|
732
799
|
declare const PAGE_SIZES: readonly number[];
|
|
733
800
|
declare const DEFAULT_PAGE_SIZE: number;
|
|
734
801
|
|
|
735
|
-
export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
|
|
802
|
+
export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
|
package/react/dist/index.js
CHANGED
|
@@ -2868,6 +2868,129 @@ function ThemeToggle({ labelled = false }) {
|
|
|
2868
2868
|
] }) : button;
|
|
2869
2869
|
}
|
|
2870
2870
|
|
|
2871
|
+
// src/Success.tsx
|
|
2872
|
+
import { forwardRef as forwardRef8, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
|
|
2873
|
+
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2874
|
+
var SuccessCheck = forwardRef8(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
|
|
2875
|
+
const circled = variant === "circled";
|
|
2876
|
+
return /* @__PURE__ */ jsxs29(
|
|
2877
|
+
"svg",
|
|
2878
|
+
{
|
|
2879
|
+
...rest,
|
|
2880
|
+
ref,
|
|
2881
|
+
className: ["ui-sx__check", `ui-sx__check--${circled ? "circled" : "line"}`, className].filter(Boolean).join(" "),
|
|
2882
|
+
width: "24",
|
|
2883
|
+
height: "24",
|
|
2884
|
+
viewBox: "0 0 24 24",
|
|
2885
|
+
"aria-hidden": "true",
|
|
2886
|
+
focusable: "false",
|
|
2887
|
+
children: [
|
|
2888
|
+
circled && /* @__PURE__ */ jsx34("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
|
|
2889
|
+
/* @__PURE__ */ jsx34("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
|
|
2890
|
+
]
|
|
2891
|
+
}
|
|
2892
|
+
);
|
|
2893
|
+
});
|
|
2894
|
+
var SuccessPanel = forwardRef8(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
|
|
2895
|
+
const circled = check === "circled";
|
|
2896
|
+
return /* @__PURE__ */ jsxs29("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
|
|
2897
|
+
/* @__PURE__ */ jsx34("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
|
|
2898
|
+
/* @__PURE__ */ jsx34("div", { className: "ui-success__title", children: title }),
|
|
2899
|
+
sub && /* @__PURE__ */ jsx34("div", { className: "ui-success__sub", children: sub })
|
|
2900
|
+
] });
|
|
2901
|
+
});
|
|
2902
|
+
var pieces = [
|
|
2903
|
+
[8, 0, -24, "a", 1],
|
|
2904
|
+
[20, 0.14, 40, "g", 0.8],
|
|
2905
|
+
[31, 0.06, 12, "c", 1.1],
|
|
2906
|
+
[42, 0.2, -52, "p", 0.9],
|
|
2907
|
+
[50, 0.02, 28, "a", 1],
|
|
2908
|
+
[58, 0.18, -16, "k", 0.75],
|
|
2909
|
+
[67, 0.09, 60, "g", 1.05],
|
|
2910
|
+
[76, 0.24, -36, "c", 0.85],
|
|
2911
|
+
[85, 0.05, 20, "a", 1],
|
|
2912
|
+
[92, 0.16, -48, "p", 0.9],
|
|
2913
|
+
[14, 0.3, 44, "k", 0.8],
|
|
2914
|
+
[37, 0.34, -28, "g", 1],
|
|
2915
|
+
[62, 0.28, 52, "a", 0.9],
|
|
2916
|
+
[80, 0.36, -20, "c", 1.05]
|
|
2917
|
+
];
|
|
2918
|
+
function Countdown({ seconds, label = "Redirecting", onDone }) {
|
|
2919
|
+
const [remaining, setRemaining] = useState13(seconds);
|
|
2920
|
+
const callback = useRef17(onDone);
|
|
2921
|
+
useEffect14(() => {
|
|
2922
|
+
callback.current = onDone;
|
|
2923
|
+
}, [onDone]);
|
|
2924
|
+
useEffect14(() => {
|
|
2925
|
+
let count = seconds;
|
|
2926
|
+
const timer = setInterval(() => {
|
|
2927
|
+
count -= 1;
|
|
2928
|
+
setRemaining(count);
|
|
2929
|
+
if (count === 0) {
|
|
2930
|
+
clearInterval(timer);
|
|
2931
|
+
callback.current?.();
|
|
2932
|
+
}
|
|
2933
|
+
}, 1e3);
|
|
2934
|
+
return () => clearInterval(timer);
|
|
2935
|
+
}, [seconds]);
|
|
2936
|
+
return /* @__PURE__ */ jsxs29("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
|
|
2937
|
+
/* @__PURE__ */ jsx34("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
|
|
2938
|
+
/* @__PURE__ */ jsxs29("span", { className: "ui-sx__count-text", children: [
|
|
2939
|
+
label,
|
|
2940
|
+
" in ",
|
|
2941
|
+
/* @__PURE__ */ jsx34("b", { "data-sx-num": "", children: remaining }),
|
|
2942
|
+
"s"
|
|
2943
|
+
] })
|
|
2944
|
+
] });
|
|
2945
|
+
}
|
|
2946
|
+
var Success = forwardRef8(function Success2({
|
|
2947
|
+
layout = "hero",
|
|
2948
|
+
level,
|
|
2949
|
+
check = "line",
|
|
2950
|
+
title = "All done",
|
|
2951
|
+
body,
|
|
2952
|
+
actions,
|
|
2953
|
+
confetti = false,
|
|
2954
|
+
countdown,
|
|
2955
|
+
onCountdownEnd,
|
|
2956
|
+
className,
|
|
2957
|
+
// Destructured only to keep it out of `rest`; see SuccessProps.eyebrow.
|
|
2958
|
+
eyebrow: _removedEyebrow,
|
|
2959
|
+
...rest
|
|
2960
|
+
}, ref) {
|
|
2961
|
+
void _removedEyebrow;
|
|
2962
|
+
const rank = [1, 2, 3, 4, 5, 6].includes(Number(level)) ? Number(level) : layout === "compact" ? 2 : 1;
|
|
2963
|
+
const Heading = `h${rank}`;
|
|
2964
|
+
const hasActions = Array.isArray(actions) ? actions.length > 0 : Boolean(actions);
|
|
2965
|
+
const seconds = countdown?.seconds;
|
|
2966
|
+
const duration = seconds != null && Number.isFinite(seconds) && seconds >= 1 ? Math.floor(seconds) : 5;
|
|
2967
|
+
const mark = check === "circled" ? "circled" : "line";
|
|
2968
|
+
return /* @__PURE__ */ jsxs29(
|
|
2969
|
+
"div",
|
|
2970
|
+
{
|
|
2971
|
+
role: "status",
|
|
2972
|
+
"aria-live": "polite",
|
|
2973
|
+
...rest,
|
|
2974
|
+
ref,
|
|
2975
|
+
className: ["ui-sx", `ui-sx--${layout}`, `ui-sx--check-${mark}`, confetti && "ui-sx--confetti", className].filter(Boolean).join(" "),
|
|
2976
|
+
children: [
|
|
2977
|
+
confetti && /* @__PURE__ */ jsx34("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
|
|
2978
|
+
([x, d, r, t, s], index) => /* @__PURE__ */ jsx34("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
|
|
2979
|
+
) }),
|
|
2980
|
+
/* @__PURE__ */ jsxs29("div", { className: "ui-sx__inner", children: [
|
|
2981
|
+
/* @__PURE__ */ jsx34("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: mark }) }),
|
|
2982
|
+
/* @__PURE__ */ jsxs29("div", { className: "ui-sx__content", children: [
|
|
2983
|
+
/* @__PURE__ */ jsx34(Heading, { className: "ui-sx__title", children: title }),
|
|
2984
|
+
body && /* @__PURE__ */ jsx34("p", { className: "ui-sx__body", children: body }),
|
|
2985
|
+
hasActions && /* @__PURE__ */ jsx34("div", { className: "ui-sx__actions", children: actions }),
|
|
2986
|
+
countdown && /* @__PURE__ */ jsx34(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
|
|
2987
|
+
] })
|
|
2988
|
+
] })
|
|
2989
|
+
]
|
|
2990
|
+
}
|
|
2991
|
+
);
|
|
2992
|
+
});
|
|
2993
|
+
|
|
2871
2994
|
// src/index.ts
|
|
2872
2995
|
var PAGE_SIZES = KIT_PAGE_SIZES;
|
|
2873
2996
|
var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
|
|
@@ -2909,6 +3032,9 @@ export {
|
|
|
2909
3032
|
Snippet,
|
|
2910
3033
|
StatBand,
|
|
2911
3034
|
StatusDot,
|
|
3035
|
+
Success,
|
|
3036
|
+
SuccessCheck,
|
|
3037
|
+
SuccessPanel,
|
|
2912
3038
|
Switch,
|
|
2913
3039
|
THEME_INIT_SCRIPT,
|
|
2914
3040
|
Tabs,
|
package/src/components/index.js
CHANGED
|
@@ -264,11 +264,13 @@ export function toast({
|
|
|
264
264
|
// a card. Two strings, no options; styles are .ui-success in styles/callout.css.
|
|
265
265
|
//
|
|
266
266
|
// The kit also publishes success() from components/success.js, which is the same
|
|
267
|
-
// idea at page size: layouts,
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
export function successPanel({ title = 'Done', sub = '' } = {}) {
|
|
271
|
-
|
|
267
|
+
// idea at page size: layouts, follow-up buttons and an auto-redirect countdown.
|
|
268
|
+
// Pick by how much of the screen the confirmation owns. The two share the check;
|
|
269
|
+
// their layout and content remain independent.
|
|
270
|
+
export function successPanel({ title = 'Done', sub = '', check = 'line' } = {}) {
|
|
271
|
+
// The circled mark is one status size everywhere, so the box narrows for it.
|
|
272
|
+
const box = check === 'circled' ? 'ui-success__check ui-success__check--circled' : 'ui-success__check';
|
|
273
|
+
return `<div class="ui-success"><div class="${box}">${successCheck(check)}</div><div class="ui-success__title">${esc(title)}</div>${sub ? `<div class="ui-success__sub">${esc(sub)}</div>` : ''}</div>`;
|
|
272
274
|
}
|
|
273
275
|
|
|
274
276
|
// ---- Empty state ---------------------------------------------------------
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// Success / confirmation surface. One factory, three layouts
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// Accent-aware, but the success mark stays on the --green family.
|
|
1
|
+
// Success / confirmation surface. One factory, three layouts, two check marks,
|
|
2
|
+
// optional confetti and an optional auto-redirect countdown, every motion path
|
|
3
|
+
// reduced-motion safe. Accent-aware, but the mark stays on the --green family.
|
|
5
4
|
//
|
|
6
5
|
// container.innerHTML = success({ title, body, actions: [{ label, variant }] });
|
|
7
6
|
//
|
|
8
|
-
// The check animation is pure CSS, so string-rendered markup animates
|
|
9
|
-
//
|
|
10
|
-
// shows the ring sweep and a static number.
|
|
7
|
+
// The check animation is pure CSS, so string-rendered markup animates once
|
|
8
|
+
// mounted. A live countdown is opt-in via wireSuccess().
|
|
11
9
|
import { esc, button } from './index.js';
|
|
12
10
|
|
|
13
|
-
//
|
|
14
|
-
// the
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
// 'line' is the bare Lucide `check`, 'circled' is Lucide circle-check-big. Which
|
|
12
|
+
// is the default, and why that departs from Guidelines / Iconography:
|
|
13
|
+
// why: docs/specification.md#success-confirmations
|
|
14
|
+
// Each is written whole because the icon-sizing and glyph-stroke gates find their
|
|
15
|
+
// subjects by scanning source for a class on an `<svg …>`: a tag split across
|
|
16
|
+
// string pieces leaves both silent rather than failing.
|
|
17
|
+
const MARKS = {
|
|
18
|
+
line: '<svg class="ui-sx__check ui-sx__check--line" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path class="ui-sx__tick" d="M20 6L9 17l-5-5"/></svg>',
|
|
19
|
+
circled: '<svg class="ui-sx__check ui-sx__check--circled" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path class="ui-sx__circle" d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path class="ui-sx__tick" d="M22 4L12 14.01l-3-3"/></svg>',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function successCheck(variant = 'line') {
|
|
23
|
+
return Object.hasOwn(MARKS, variant) ? MARKS[variant] : MARKS.line;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// A deterministic confetti field (opt-in). Decorative + aria-hidden; each piece
|
|
@@ -60,21 +60,26 @@ function countdownEl({ seconds = 5, label = 'Redirecting' } = {}) {
|
|
|
60
60
|
// For a confirmation that stays inside the page the user is already on, the kit
|
|
61
61
|
// publishes successPanel() from components/index.js — a check, a title and one
|
|
62
62
|
// line of sub, and nothing to configure. Pick by how much of the screen the
|
|
63
|
-
// confirmation owns. The two share the
|
|
64
|
-
//
|
|
63
|
+
// confirmation owns. The two share the check, while their layout and content
|
|
64
|
+
// remain independent.
|
|
65
|
+
//
|
|
66
|
+
// Both carry one title and at most one short line under it. There is no eyebrow
|
|
67
|
+
// tier: a confirmation stacking a label, a headline and a paragraph reads as
|
|
68
|
+
// three competing voices for one outcome.
|
|
69
|
+
// why: docs/specification.md#success-confirmations
|
|
65
70
|
export function success({
|
|
66
71
|
layout = 'hero', // 'hero' | 'split' | 'compact'
|
|
67
72
|
level, // heading level of the title; see the note below
|
|
68
|
-
|
|
69
|
-
eyebrow = '',
|
|
73
|
+
check = 'line', // 'line' | 'circled' — see successCheck() above
|
|
70
74
|
title = 'All done',
|
|
71
|
-
body = '',
|
|
75
|
+
body = '', // one short line, or nothing; see the note below
|
|
72
76
|
actions = [], // [{ label, variant, href, icon, iconRight, size }]
|
|
73
77
|
confetti = false, // opt-in particle burst (reduced-motion safe)
|
|
74
78
|
countdown = null, // { seconds, label } | null
|
|
75
79
|
className = '',
|
|
76
80
|
} = {}) {
|
|
77
|
-
const
|
|
81
|
+
const mark = check === 'circled' ? 'circled' : 'line';
|
|
82
|
+
const cls = ['ui-sx', `ui-sx--${layout}`, `ui-sx--check-${mark}`, confetti && 'ui-sx--confetti', className]
|
|
78
83
|
.filter(Boolean).join(' ');
|
|
79
84
|
|
|
80
85
|
// The title's rank follows the layout, because the layout is the question
|
|
@@ -87,16 +92,6 @@ export function success({
|
|
|
87
92
|
const rank = [1, 2, 3, 4, 5, 6].includes(Number(level)) ? Number(level) : (layout === 'compact' ? 2 : 1);
|
|
88
93
|
const h = `h${rank}`;
|
|
89
94
|
|
|
90
|
-
const bd = backdrop === 'aurora'
|
|
91
|
-
? `<div class="ui-sx__aurora" aria-hidden="true">
|
|
92
|
-
<span class="ui-sx__glow ui-sx__glow--a"></span>
|
|
93
|
-
<span class="ui-sx__glow ui-sx__glow--b"></span>
|
|
94
|
-
</div>`
|
|
95
|
-
: backdrop === 'glow'
|
|
96
|
-
? `<span class="ui-glow ui-glow--green ui-sx__bg-glow" aria-hidden="true"></span>`
|
|
97
|
-
: '';
|
|
98
|
-
|
|
99
|
-
const eyebrowEl = eyebrow ? `<div class="ui-sx__eyebrow">${esc(eyebrow)}</div>` : '';
|
|
100
95
|
const bodyEl = body ? `<p class="ui-sx__body">${esc(body)}</p>` : '';
|
|
101
96
|
const actionsEl = actions.length
|
|
102
97
|
? `<div class="ui-sx__actions">${actions.map((a) => button({ size: 'md', ...a })).join('')}</div>`
|
|
@@ -104,12 +99,10 @@ export function success({
|
|
|
104
99
|
const countEl = countdown ? countdownEl(countdown) : '';
|
|
105
100
|
|
|
106
101
|
return `<div class="${esc(cls)}" role="status" aria-live="polite">
|
|
107
|
-
${bd}
|
|
108
102
|
${confetti ? confettiField() : ''}
|
|
109
103
|
<div class="ui-sx__inner">
|
|
110
|
-
<div class="ui-sx__visual">${successCheck()}</div>
|
|
104
|
+
<div class="ui-sx__visual">${successCheck(mark)}</div>
|
|
111
105
|
<div class="ui-sx__content">
|
|
112
|
-
${eyebrowEl}
|
|
113
106
|
<${h} class="ui-sx__title">${esc(title)}</${h}>
|
|
114
107
|
${bodyEl}
|
|
115
108
|
${actionsEl}
|
package/src/index.d.ts
CHANGED
|
@@ -82,7 +82,7 @@ export declare function deckTextSwitch(active?: string): string;
|
|
|
82
82
|
export declare function hlShell(raw: string): string;
|
|
83
83
|
export declare function pill(label: string, variant?: string): string;
|
|
84
84
|
export declare function statusDot(live?: boolean): string;
|
|
85
|
-
export declare function successCheck(): string;
|
|
85
|
+
export declare function successCheck(variant?: 'line' | 'circled'): string;
|
|
86
86
|
export declare function themeIcon(theme: string): string;
|
|
87
87
|
export declare function themeName(theme: string): string;
|
|
88
88
|
export declare function themeToggle(theme?: string): string;
|
package/src/styles/callout.css
CHANGED
|
@@ -123,9 +123,15 @@
|
|
|
123
123
|
}
|
|
124
124
|
.ui-success__check {
|
|
125
125
|
--sx-green: var(--chip-success-ink);
|
|
126
|
-
width:
|
|
126
|
+
width: 28px; height: 28px;
|
|
127
127
|
margin: 0 auto 14px;
|
|
128
128
|
}
|
|
129
129
|
.ui-success__check .ui-sx__check { width: 100%; height: 100%; }
|
|
130
|
+
/* `circled` is one status size everywhere, this panel included: the box narrows to
|
|
131
|
+
the 20px success() draws it at, rather than stretching a status mark to fill the
|
|
132
|
+
line mark's box. The mark fills the box, so the box is what decides. The modifier
|
|
133
|
+
is written by successPanel(), as success() writes ui-sx--check-circled on its root.
|
|
134
|
+
why: docs/specification.md#success-confirmations */
|
|
135
|
+
.ui-success__check--circled { width: 20px; height: 20px; }
|
|
130
136
|
.ui-success__title { color: var(--strong); font-size: var(--text-lg); font-weight: var(--weight-semibold); margin-bottom: 5px; }
|
|
131
137
|
.ui-success__sub { color: var(--text); font-size: var(--text-base); }
|
package/src/styles/success.css
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
/* ============================================================================
|
|
2
2
|
* Success / confirmation surface — .ui-sx*
|
|
3
|
-
* The
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* layouts (hero / split / compact) and three backdrops (aurora / glow / flat).
|
|
3
|
+
* The end of a flow: a check mark, the outcome in words, somewhere to go
|
|
4
|
+
* next, an optional confetti field and an optional auto-redirect countdown.
|
|
5
|
+
* Three layouts (hero / split / compact) and two check marks.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the foot. Markup comes from success() in components/index
|
|
7
|
+
* The card is a plain elevated surface — no blurred glows or drifting blobs
|
|
8
|
+
* behind it. Accent-aware via the kit tokens; the mark stays on the --green
|
|
9
|
+
* family (green = success). Every motion path is reduced-motion safe — see
|
|
10
|
+
* the block at the foot. Markup comes from success() in components/index
|
|
11
|
+
* (re-exported).
|
|
12
12
|
* ========================================================================== */
|
|
13
13
|
|
|
14
|
-
.ui-sx, .ui-success__check {
|
|
15
|
-
--sx-tint: color-mix(in srgb, var(--green) 18%, transparent);
|
|
16
|
-
--sx-track: color-mix(in srgb, var(--green) 32%, transparent);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
.ui-sx {
|
|
20
15
|
--sx-green: var(--green);
|
|
21
16
|
position: relative;
|
|
@@ -26,41 +21,9 @@
|
|
|
26
21
|
isolation: isolate;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
/* ---- Backdrops ----------------------------------------------------------- */
|
|
30
|
-
/* Aurora: two soft blurred blobs — a green wash under the check + an accent
|
|
31
|
-
blob for identity — so the success matches the rest of the kit's warmth. */
|
|
32
|
-
.ui-sx__aurora { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
|
|
33
|
-
.ui-sx__glow {
|
|
34
|
-
position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.9;
|
|
35
|
-
aspect-ratio: 1; will-change: transform;
|
|
36
|
-
}
|
|
37
|
-
.ui-sx__glow--a {
|
|
38
|
-
width: 58%; top: -14%; left: 50%; translate: -50% 0;
|
|
39
|
-
background: radial-gradient(circle at center, var(--sx-tint), transparent 68%);
|
|
40
|
-
animation: ui-sx-float 14s ease-in-out infinite alternate; /* motion: ambient — a background glow that drifts on its own clock; `alternate` needs a symmetric curve, which var(--ease) is not */
|
|
41
|
-
}
|
|
42
|
-
.ui-sx__glow--b {
|
|
43
|
-
width: 52%; bottom: -20%; left: 12%;
|
|
44
|
-
background: radial-gradient(circle at center,
|
|
45
|
-
color-mix(in srgb, var(--accent) 30%, transparent), transparent 68%);
|
|
46
|
-
animation: ui-sx-float 18s ease-in-out -6s infinite alternate; /* motion: ambient — the second glow, offset in period and phase so the pair never lines up */
|
|
47
|
-
}
|
|
48
|
-
:root[data-theme="light"] .ui-sx__glow { opacity: 0.7; }
|
|
49
|
-
@keyframes ui-sx-float {
|
|
50
|
-
from { transform: translate(-2%, -2%) scale(1); }
|
|
51
|
-
to { transform: translate(3%, 3%) scale(1.14); }
|
|
52
|
-
}
|
|
53
|
-
/* Glow backdrop: reuse the shared .ui-glow--green, parked behind the check. */
|
|
54
|
-
.ui-sx__bg-glow { top: -40%; left: 50%; translate: -50% 0; width: 360px; height: 360px; }
|
|
55
|
-
|
|
56
24
|
/* ---- Layout shell -------------------------------------------------------- */
|
|
57
25
|
.ui-sx__inner { position: relative; z-index: 1; }
|
|
58
26
|
.ui-sx__content { min-width: 0; }
|
|
59
|
-
.ui-sx__eyebrow {
|
|
60
|
-
/* rank: label */
|
|
61
|
-
color: var(--green); font-size: var(--text-sm); font-weight: var(--weight-medium);
|
|
62
|
-
margin-bottom: 10px;
|
|
63
|
-
}
|
|
64
27
|
.ui-sx__title {
|
|
65
28
|
color: var(--strong); font-size: var(--text-xl); font-weight: var(--weight-semibold);
|
|
66
29
|
letter-spacing: -.02em; line-height: 1.2; margin: 0;
|
|
@@ -76,7 +39,7 @@
|
|
|
76
39
|
.ui-sx--hero .ui-sx__visual { margin-bottom: 22px; }
|
|
77
40
|
.ui-sx--hero .ui-sx__body { margin-left: auto; margin-right: auto; }
|
|
78
41
|
.ui-sx--hero .ui-sx__actions { justify-content: center; }
|
|
79
|
-
.ui-sx--hero .ui-sx__check { width:
|
|
42
|
+
.ui-sx--hero .ui-sx__check { width: 56px; height: 56px; }
|
|
80
43
|
|
|
81
44
|
/* split — visual panel beside the copy */
|
|
82
45
|
.ui-sx--split .ui-sx__inner {
|
|
@@ -87,14 +50,14 @@
|
|
|
87
50
|
background: color-mix(in srgb, var(--green) 8%, transparent); /* ring-gap: inherit — decorative illustration has no nested focus target. */
|
|
88
51
|
border-right: 1px solid var(--border);
|
|
89
52
|
}
|
|
90
|
-
.ui-sx--split .ui-sx__check { width:
|
|
53
|
+
.ui-sx--split .ui-sx__check { width: 72px; height: 72px; }
|
|
91
54
|
.ui-sx--split .ui-sx__content { padding: 40px 36px; align-self: center; }
|
|
92
55
|
|
|
93
56
|
/* compact — inline, panel/toast-adjacent */
|
|
94
57
|
.ui-sx--compact { padding: 18px 20px; }
|
|
95
58
|
.ui-sx--compact .ui-sx__inner { display: flex; align-items: center; gap: 16px; }
|
|
96
59
|
.ui-sx--compact .ui-sx__visual { flex: none; }
|
|
97
|
-
.ui-sx--compact .ui-sx__check { width:
|
|
60
|
+
.ui-sx--compact .ui-sx__check { width: 28px; height: 28px; }
|
|
98
61
|
.ui-sx--compact .ui-sx__title { font-size: calc(var(--text-md) + 0.5px); }
|
|
99
62
|
.ui-sx--compact .ui-sx__body { font-size: var(--text-sm); margin-top: 3px; }
|
|
100
63
|
.ui-sx--compact .ui-sx__actions { margin-top: 0; margin-left: auto; flex: none; }
|
|
@@ -102,38 +65,29 @@
|
|
|
102
65
|
.ui-sx--compact .ui-sx__content > div:first-child,
|
|
103
66
|
.ui-sx--compact .ui-sx__title { min-width: 0; }
|
|
104
67
|
|
|
105
|
-
/* ---- The
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.ui-sx__tick {
|
|
114
|
-
fill: none; stroke: var(--sx-green); stroke-width:
|
|
68
|
+
/* ---- The check mark ------------------------------------------------------ */
|
|
69
|
+
/* Two marks, both unmodified Lucide paths at Lucide's own stroke width, in the
|
|
70
|
+
success colour. `line` is the bare check (Lucide `check`), the default. It
|
|
71
|
+
strokes itself on via dash-offset; the path is 22.6 units long, which is what
|
|
72
|
+
the dasharray is tuned to. `circled` is Lucide `circle-check-big` at 20px —
|
|
73
|
+
the kit's label size — for a confirmation that should stay quiet. It is drawn
|
|
74
|
+
at rest, because a 20px mark has nothing to choreograph. */
|
|
75
|
+
.ui-sx__check { display: block; }
|
|
76
|
+
.ui-sx__tick, .ui-sx__circle {
|
|
77
|
+
fill: none; stroke: var(--sx-green, var(--green)); stroke-width: 2;
|
|
115
78
|
stroke-linecap: round; stroke-linejoin: round;
|
|
116
|
-
stroke-dasharray: 34; stroke-dashoffset: 34;
|
|
117
|
-
animation: ui-sx-draw .5s cubic-bezier(.6,0,.35,1) .28s forwards; /* motion: choreographed — the tick draws while the disc is still settling */
|
|
118
|
-
filter: drop-shadow(0 0 5px color-mix(in srgb, var(--green) 55%, transparent));
|
|
119
|
-
}
|
|
120
|
-
/* Burst ring that expands and fades once, framing the moment. */
|
|
121
|
-
.ui-sx__ring {
|
|
122
|
-
fill: none; stroke: var(--sx-green); stroke-width: 2;
|
|
123
|
-
transform-origin: center; opacity: 0;
|
|
124
|
-
animation: ui-sx-burst .7s cubic-bezier(.3,.7,.4,1) .2s forwards; /* motion: choreographed — the ring outlives the tick so the moment ends on the frame */
|
|
125
79
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
60% { transform: scale(1.06); opacity: 1; }
|
|
130
|
-
100% { transform: scale(1); }
|
|
80
|
+
.ui-sx__check--line .ui-sx__tick {
|
|
81
|
+
stroke-dasharray: 23; stroke-dashoffset: 23;
|
|
82
|
+
animation: ui-sx-draw var(--dur-slow) var(--ease-out) forwards;
|
|
131
83
|
}
|
|
132
84
|
@keyframes ui-sx-draw { to { stroke-dashoffset: 0; } }
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
85
|
+
|
|
86
|
+
/* circled — one small size in every layout, so it reads as a status mark
|
|
87
|
+
rather than an illustration. The split panel narrows to match it. */
|
|
88
|
+
.ui-sx--check-circled .ui-sx__check { width: 20px; height: 20px; }
|
|
89
|
+
.ui-sx--check-circled.ui-sx--hero .ui-sx__visual { margin-bottom: 12px; }
|
|
90
|
+
.ui-sx--check-circled.ui-sx--split .ui-sx__inner { grid-template-columns: minmax(120px, 160px) 1fr; }
|
|
137
91
|
|
|
138
92
|
/* ---- Confetti (opt-in) --------------------------------------------------- */
|
|
139
93
|
.ui-sx__confetti { position: absolute; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
|
|
@@ -171,12 +125,9 @@
|
|
|
171
125
|
.ui-sx__count-text { color: var(--text); font-size: var(--text-sm); }
|
|
172
126
|
.ui-sx__count-text b { color: var(--text); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
|
|
173
127
|
|
|
174
|
-
/* ---- Reduced motion — static check, no
|
|
128
|
+
/* ---- Reduced motion — static check, no confetti / sweep ------------------ */
|
|
175
129
|
@media (prefers-reduced-motion: reduce) {
|
|
176
|
-
.ui-
|
|
177
|
-
.ui-sx__disc { animation: none; }
|
|
178
|
-
.ui-sx__tick { animation: none; stroke-dashoffset: 0; }
|
|
179
|
-
.ui-sx__ring { display: none; }
|
|
130
|
+
.ui-sx__check--line .ui-sx__tick { animation: none; stroke-dashoffset: 0; }
|
|
180
131
|
.ui-sx__confetti { display: none; }
|
|
181
132
|
.ui-sx__count-ring { animation: none; background: var(--surface-2); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
|
|
182
133
|
}
|
|
@@ -184,6 +135,7 @@
|
|
|
184
135
|
/* ---- Narrow — split stacks -------------------------------------------- */
|
|
185
136
|
@media (max-width: 560px) {
|
|
186
137
|
.ui-sx--split .ui-sx__inner { grid-template-columns: 1fr; }
|
|
138
|
+
.ui-sx--check-circled.ui-sx--split .ui-sx__inner { grid-template-columns: 1fr; }
|
|
187
139
|
.ui-sx--split .ui-sx__visual { border-right: 0; border-bottom: 1px solid var(--border); padding: 32px 24px; }
|
|
188
140
|
.ui-sx--split .ui-sx__content { padding: 28px 26px; text-align: center; }
|
|
189
141
|
.ui-sx--split .ui-sx__actions { justify-content: center; }
|