@lyeve-labs/ui-kit 0.39.1 → 0.40.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/dist/components/Field.svelte +12 -6
- package/dist/components/Field.svelte.d.ts +6 -2
- package/dist/components/Panel.svelte +38 -8
- package/dist/components/Panel.svelte.d.ts +13 -0
- package/dist/components/SegmentedControl.svelte +9 -3
- package/dist/components/SegmentedControl.svelte.d.ts +7 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/choice.d.ts +4 -2
- package/dist/internal/choice.js +2 -1
- package/dist/internal/icon.d.ts +12 -0
- package/dist/internal/icon.js +6 -0
- package/dist/internal/layout.d.ts +9 -0
- package/dist/internal/layout.js +9 -0
- package/dist/styles/theme.css +14 -0
- package/package.json +1 -1
- package/src/lib/styles/theme.css +14 -0
|
@@ -48,8 +48,12 @@
|
|
|
48
48
|
/**
|
|
49
49
|
* The control is a group, a `SegmentedControl`, `FilterChips` or a row of
|
|
50
50
|
* your own. A label's `for` reaches one form control and never a group, so
|
|
51
|
-
* the caption is drawn as text with an id instead,
|
|
52
|
-
* name from it
|
|
51
|
+
* the caption is drawn as text with an id instead, handed over as
|
|
52
|
+
* `labeledBy`. A `SegmentedControl` takes its name from it, and draws no
|
|
53
|
+
* caption of its own once given `labelHidden` as well. `FilterChips` takes
|
|
54
|
+
* no `labeledBy`. It names its group from its own `label`, so give that
|
|
55
|
+
* label the caption's words. A row of your own points `aria-labelledby` at
|
|
56
|
+
* the caption.
|
|
53
57
|
*/
|
|
54
58
|
group?: boolean;
|
|
55
59
|
hint?: string;
|
|
@@ -98,10 +102,12 @@
|
|
|
98
102
|
<div data-field class="{FIELD_WRAP} {klass}">
|
|
99
103
|
{#if label}
|
|
100
104
|
<!--
|
|
101
|
-
A hidden label stays a real label with a real `for
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
A hidden label stays in the page: a real label with a real `for`, or for
|
|
106
|
+
a group a caption with an id that a SegmentedControl or a row of your own
|
|
107
|
+
points at. Swapping it for an aria-label on the control would name the
|
|
108
|
+
control and drop the name out of the reading order, so a reader moving
|
|
109
|
+
through a row of them would meet the controls with nothing ahead of them
|
|
110
|
+
saying what each one is.
|
|
105
111
|
-->
|
|
106
112
|
{#if group}
|
|
107
113
|
<span id="{fieldId}-label" class="{FIELD_LABEL} {labelHidden ? 'sr-only' : ''}">
|
|
@@ -36,8 +36,12 @@ interface Props {
|
|
|
36
36
|
/**
|
|
37
37
|
* The control is a group, a `SegmentedControl`, `FilterChips` or a row of
|
|
38
38
|
* your own. A label's `for` reaches one form control and never a group, so
|
|
39
|
-
* the caption is drawn as text with an id instead,
|
|
40
|
-
* name from it
|
|
39
|
+
* the caption is drawn as text with an id instead, handed over as
|
|
40
|
+
* `labeledBy`. A `SegmentedControl` takes its name from it, and draws no
|
|
41
|
+
* caption of its own once given `labelHidden` as well. `FilterChips` takes
|
|
42
|
+
* no `labeledBy`. It names its group from its own `label`, so give that
|
|
43
|
+
* label the caption's words. A row of your own points `aria-labelledby` at
|
|
44
|
+
* the caption.
|
|
41
45
|
*/
|
|
42
46
|
group?: boolean;
|
|
43
47
|
hint?: string;
|
|
@@ -14,12 +14,28 @@
|
|
|
14
14
|
*
|
|
15
15
|
* The heading is a real h2 or h3. A page's outline is what a screen reader
|
|
16
16
|
* navigates by, and a styled div appears in no heading list.
|
|
17
|
+
*
|
|
18
|
+
* `tone="danger"` is the surface for destructive actions such as deleting an
|
|
19
|
+
* account. Alert and Banner are live regions, so a form inside one is read
|
|
20
|
+
* out on every change, and apps drew this box by hand instead. The tone
|
|
21
|
+
* colors the heading and its icon, and the box when there is one. With
|
|
22
|
+
* `variant="plain"` there is no box, so only the heading carries the tone:
|
|
23
|
+
* the variant decides whether a box is drawn and the tone decides its color,
|
|
24
|
+
* and neither prop silently undoes the other. That plain form is the danger
|
|
25
|
+
* group inside a card that already draws the box. The description stays
|
|
26
|
+
* muted, and the contrast suite measures every text token on the surface.
|
|
17
27
|
*/
|
|
18
28
|
import type { Component, Snippet } from 'svelte';
|
|
19
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
CARD_PAD,
|
|
31
|
+
CARD_SURFACE,
|
|
32
|
+
PANEL_DANGER_SURFACE,
|
|
33
|
+
sectionHeading,
|
|
34
|
+
} from '../internal/layout.js';
|
|
20
35
|
|
|
21
36
|
type Pad = 'none' | 'sm' | 'md';
|
|
22
37
|
type Variant = 'bordered' | 'plain';
|
|
38
|
+
type Tone = 'neutral' | 'danger';
|
|
23
39
|
|
|
24
40
|
interface Props {
|
|
25
41
|
heading?: string;
|
|
@@ -31,6 +47,8 @@
|
|
|
31
47
|
pad?: Pad;
|
|
32
48
|
/** A quiet variant with no border, for a grouping that needs separation but not a box. */
|
|
33
49
|
variant?: Variant;
|
|
50
|
+
/** `danger` for a group of destructive actions. It composes with `plain`, see above. */
|
|
51
|
+
tone?: Tone;
|
|
34
52
|
class?: string;
|
|
35
53
|
actions?: Snippet;
|
|
36
54
|
children: Snippet;
|
|
@@ -43,6 +61,7 @@
|
|
|
43
61
|
icon = undefined,
|
|
44
62
|
pad = 'md',
|
|
45
63
|
variant = 'bordered',
|
|
64
|
+
tone = 'neutral',
|
|
46
65
|
class: klass = '',
|
|
47
66
|
actions,
|
|
48
67
|
children,
|
|
@@ -55,9 +74,9 @@
|
|
|
55
74
|
* and which of them wins is decided by the order Tailwind emits them and not
|
|
56
75
|
* by the order they were written here.
|
|
57
76
|
*/
|
|
58
|
-
const SURFACE: Record<Variant, string
|
|
59
|
-
bordered: CARD_SURFACE,
|
|
60
|
-
plain: '',
|
|
77
|
+
const SURFACE: Record<Tone, Record<Variant, string>> = {
|
|
78
|
+
neutral: { bordered: CARD_SURFACE, plain: '' },
|
|
79
|
+
danger: { bordered: PANEL_DANGER_SURFACE, plain: '' },
|
|
61
80
|
};
|
|
62
81
|
|
|
63
82
|
/**
|
|
@@ -65,14 +84,25 @@
|
|
|
65
84
|
* padding change. A plain panel usually takes `pad="none"`: with no box to
|
|
66
85
|
* inset from, an inset only pushes its heading out of line with the page.
|
|
67
86
|
*/
|
|
68
|
-
const surface = $derived(`${SURFACE[variant]} ${CARD_PAD[pad]}`.trim());
|
|
87
|
+
const surface = $derived(`${SURFACE[tone][variant]} ${CARD_PAD[pad]}`.trim());
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The level's treatment with its one text color swapped, not a second color
|
|
91
|
+
* added after it: two utilities for one property resolve by emit order.
|
|
92
|
+
*/
|
|
93
|
+
const headingClass = (level: 2 | 3) =>
|
|
94
|
+
tone === 'danger'
|
|
95
|
+
? sectionHeading(level).replace(/\btext-fg\b/, 'text-danger')
|
|
96
|
+
: sectionHeading(level);
|
|
69
97
|
</script>
|
|
70
98
|
|
|
71
99
|
<div data-print="keep" class="flex flex-col gap-stack {surface} {klass}">
|
|
72
100
|
{#snippet headingText()}
|
|
73
101
|
{#if icon}
|
|
74
102
|
{@const Icon = icon}
|
|
75
|
-
<span class="shrink-0 text-muted" aria-hidden="true"
|
|
103
|
+
<span class="shrink-0 {tone === 'danger' ? 'text-danger' : 'text-muted'}" aria-hidden="true"
|
|
104
|
+
><Icon size={16} /></span
|
|
105
|
+
>
|
|
76
106
|
{/if}
|
|
77
107
|
{heading}
|
|
78
108
|
{/snippet}
|
|
@@ -84,9 +114,9 @@
|
|
|
84
114
|
document structure decision, and both headings this can produce
|
|
85
115
|
should be readable in the file. -->
|
|
86
116
|
{#if heading && headingLevel === 2}
|
|
87
|
-
<h2 class="{
|
|
117
|
+
<h2 class="{headingClass(2)} flex items-center gap-2">{@render headingText()}</h2>
|
|
88
118
|
{:else if heading}
|
|
89
|
-
<h3 class="{
|
|
119
|
+
<h3 class="{headingClass(3)} flex items-center gap-2">{@render headingText()}</h3>
|
|
90
120
|
{/if}
|
|
91
121
|
{#if description}<p class="text-sm text-muted">{description}</p>{/if}
|
|
92
122
|
</div>
|
|
@@ -13,10 +13,21 @@
|
|
|
13
13
|
*
|
|
14
14
|
* The heading is a real h2 or h3. A page's outline is what a screen reader
|
|
15
15
|
* navigates by, and a styled div appears in no heading list.
|
|
16
|
+
*
|
|
17
|
+
* `tone="danger"` is the surface for destructive actions such as deleting an
|
|
18
|
+
* account. Alert and Banner are live regions, so a form inside one is read
|
|
19
|
+
* out on every change, and apps drew this box by hand instead. The tone
|
|
20
|
+
* colors the heading and its icon, and the box when there is one. With
|
|
21
|
+
* `variant="plain"` there is no box, so only the heading carries the tone:
|
|
22
|
+
* the variant decides whether a box is drawn and the tone decides its color,
|
|
23
|
+
* and neither prop silently undoes the other. That plain form is the danger
|
|
24
|
+
* group inside a card that already draws the box. The description stays
|
|
25
|
+
* muted, and the contrast suite measures every text token on the surface.
|
|
16
26
|
*/
|
|
17
27
|
import type { Component, Snippet } from 'svelte';
|
|
18
28
|
type Pad = 'none' | 'sm' | 'md';
|
|
19
29
|
type Variant = 'bordered' | 'plain';
|
|
30
|
+
type Tone = 'neutral' | 'danger';
|
|
20
31
|
interface Props {
|
|
21
32
|
heading?: string;
|
|
22
33
|
/** 2 directly under a page title, 3 inside a card or another panel. */
|
|
@@ -30,6 +41,8 @@ interface Props {
|
|
|
30
41
|
pad?: Pad;
|
|
31
42
|
/** A quiet variant with no border, for a grouping that needs separation but not a box. */
|
|
32
43
|
variant?: Variant;
|
|
44
|
+
/** `danger` for a group of destructive actions. It composes with `plain`, see above. */
|
|
45
|
+
tone?: Tone;
|
|
33
46
|
class?: string;
|
|
34
47
|
actions?: Snippet;
|
|
35
48
|
children: Snippet;
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
joinIds,
|
|
31
31
|
} from '../internal/field.js';
|
|
32
32
|
import { safeHref } from '../internal/href.js';
|
|
33
|
+
import type { IconPx } from '../internal/icon.js';
|
|
33
34
|
import { HIT_AREA } from '../internal/touch.js';
|
|
34
35
|
|
|
35
36
|
type Size = 'sm' | 'md';
|
|
@@ -44,7 +45,11 @@
|
|
|
44
45
|
* is for from the segments alone.
|
|
45
46
|
*/
|
|
46
47
|
label: string;
|
|
47
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Drops the caption above the row. The group keeps its accessible name.
|
|
50
|
+
* Pass it with `labeledBy`, or the row draws its own caption as well as
|
|
51
|
+
* the one it is named by.
|
|
52
|
+
*/
|
|
48
53
|
labelHidden?: boolean;
|
|
49
54
|
/**
|
|
50
55
|
* The group's id. The hint and the error take theirs from it. Left unset,
|
|
@@ -66,7 +71,8 @@
|
|
|
66
71
|
describedBy?: string;
|
|
67
72
|
/**
|
|
68
73
|
* The id of a caption drawn outside the row, a `Field` one for instance.
|
|
69
|
-
* The group takes its name from it in place of `label`.
|
|
74
|
+
* The group takes its name from it in place of `label`. The row still draws
|
|
75
|
+
* `label` above itself until `labelHidden` is set too.
|
|
70
76
|
*/
|
|
71
77
|
labeledBy?: string;
|
|
72
78
|
/** Submits the value with the surrounding form, through a hidden input. */
|
|
@@ -110,7 +116,7 @@
|
|
|
110
116
|
* short label such as "6h" takes a 44px target across as well, and the hit
|
|
111
117
|
* boxes of two neighbors do not overlap.
|
|
112
118
|
*/
|
|
113
|
-
const SIZES: Record<Size, { row: string; segment: string; icon:
|
|
119
|
+
const SIZES: Record<Size, { row: string; segment: string; icon: IconPx }> = {
|
|
114
120
|
sm: { row: 'h-8 coarse:min-h-control', segment: 'gap-1.5 px-2.5 text-xs', icon: 14 },
|
|
115
121
|
md: { row: 'h-control', segment: 'gap-2 px-3 text-sm', icon: 16 },
|
|
116
122
|
};
|
|
@@ -30,7 +30,11 @@ declare function $$render<T extends string>(): {
|
|
|
30
30
|
* is for from the segments alone.
|
|
31
31
|
*/
|
|
32
32
|
label: string;
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Drops the caption above the row. The group keeps its accessible name.
|
|
35
|
+
* Pass it with `labeledBy`, or the row draws its own caption as well as
|
|
36
|
+
* the one it is named by.
|
|
37
|
+
*/
|
|
34
38
|
labelHidden?: boolean;
|
|
35
39
|
/**
|
|
36
40
|
* The group's id. The hint and the error take theirs from it. Left unset,
|
|
@@ -52,7 +56,8 @@ declare function $$render<T extends string>(): {
|
|
|
52
56
|
describedBy?: string;
|
|
53
57
|
/**
|
|
54
58
|
* The id of a caption drawn outside the row, a `Field` one for instance.
|
|
55
|
-
* The group takes its name from it in place of `label`.
|
|
59
|
+
* The group takes its name from it in place of `label`. The row still draws
|
|
60
|
+
* `label` above itself until `labelHidden` is set too.
|
|
56
61
|
*/
|
|
57
62
|
labeledBy?: string;
|
|
58
63
|
/** Submits the value with the surrounding form, through a hidden input. */
|
package/dist/index.d.ts
CHANGED
|
@@ -103,4 +103,4 @@ export { cn, type ClassValue } from './utils/cn.js';
|
|
|
103
103
|
export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setTheme, setThemePreference, systemTheme, themeBootScript, watchThemePreference, toggleTheme, watchSystemTheme, THEME_PREFERENCES, type Theme, type ThemePreference, } from './utils/theme.js';
|
|
104
104
|
export * as motion from './motion.js';
|
|
105
105
|
export type { Rung as MotionRung, Curve as MotionCurve } from './motion.js';
|
|
106
|
-
export declare const VERSION = "0.
|
|
106
|
+
export declare const VERSION = "0.40.0";
|
package/dist/index.js
CHANGED
|
@@ -116,4 +116,4 @@ export * as motion from './motion.js';
|
|
|
116
116
|
// ── Version ────────────────────────────────────────────────────────────────
|
|
117
117
|
// Generated from package.json by `pnpm version:sync`. Bump package.json, never
|
|
118
118
|
// this line. The build and the test suite fail when the two disagree.
|
|
119
|
-
export const VERSION = '0.
|
|
119
|
+
export const VERSION = '0.40.0';
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Not exported from the package entry point: this is an implementation detail.
|
|
16
16
|
*/
|
|
17
|
+
import type { IconPx } from './icon.js';
|
|
17
18
|
/** The scale of the painted box: sm 14px, md 16px, lg 20px. */
|
|
18
19
|
export type ChoiceSize = 'sm' | 'md' | 'lg';
|
|
19
20
|
/** Inline is a box beside a label. Card is a bordered option surface. */
|
|
@@ -100,9 +101,10 @@ export declare function choiceIcon(size: ChoiceSize): string;
|
|
|
100
101
|
*
|
|
101
102
|
* A lucide icon takes a number, not a class, so the slot above cannot size it.
|
|
102
103
|
* These are the same 14, 16 and 20 the slot reserves. An icon rendered at any
|
|
103
|
-
* other size overflows the slot or floats inside it.
|
|
104
|
+
* other size overflows the slot or floats inside it. Each is a rung of the
|
|
105
|
+
* icon ladder, which the type holds.
|
|
104
106
|
*/
|
|
105
|
-
export declare const CHOICE_ICON_PX: Record<ChoiceSize,
|
|
107
|
+
export declare const CHOICE_ICON_PX: Record<ChoiceSize, IconPx>;
|
|
106
108
|
/**
|
|
107
109
|
* The fieldset a group renders, so the set is announced as a group rather than
|
|
108
110
|
* as loose controls.
|
package/dist/internal/choice.js
CHANGED
|
@@ -142,7 +142,8 @@ export function choiceIcon(size) {
|
|
|
142
142
|
*
|
|
143
143
|
* A lucide icon takes a number, not a class, so the slot above cannot size it.
|
|
144
144
|
* These are the same 14, 16 and 20 the slot reserves. An icon rendered at any
|
|
145
|
-
* other size overflows the slot or floats inside it.
|
|
145
|
+
* other size overflows the slot or floats inside it. Each is a rung of the
|
|
146
|
+
* icon ladder, which the type holds.
|
|
146
147
|
*/
|
|
147
148
|
export const CHOICE_ICON_PX = {
|
|
148
149
|
sm: 14,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five sizes an icon may take. Each console names the same ladder in its
|
|
3
|
+
* own icon.ts, and the consistency suite holds every literal size in the kit
|
|
4
|
+
* to this list.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ICON_LADDER: readonly [12, 14, 16, 20, 40];
|
|
7
|
+
/**
|
|
8
|
+
* A rung of the icon ladder. A size a component looks up in a table is typed
|
|
9
|
+
* with it, so an entry off the ladder fails the type check where the suite
|
|
10
|
+
* cannot read a number held in a variable.
|
|
11
|
+
*/
|
|
12
|
+
export type IconPx = (typeof ICON_LADDER)[number];
|
|
@@ -93,6 +93,15 @@ export declare const PAGE_STACK = "flex flex-col gap-section";
|
|
|
93
93
|
* crops the ring of every button inside it down to whichever edge fits.
|
|
94
94
|
*/
|
|
95
95
|
export declare const CARD_SURFACE = "bg-surface border border-line rounded-xl";
|
|
96
|
+
/**
|
|
97
|
+
* The card surface in the danger tone, for a group of destructive actions.
|
|
98
|
+
*
|
|
99
|
+
* The fill is danger mixed into surface rather than a translucent tint, so it
|
|
100
|
+
* is opaque and paints the same on the page ground and inside a card. Five
|
|
101
|
+
* percent keeps the controls of a form inside it on a ground they were drawn
|
|
102
|
+
* for. The contrast suite reads the share from this string.
|
|
103
|
+
*/
|
|
104
|
+
export declare const PANEL_DANGER_SURFACE = "bg-[color-mix(in_srgb,var(--color-danger)_5%,var(--color-surface))] border border-danger/40 rounded-xl";
|
|
96
105
|
/**
|
|
97
106
|
* Card padding by name.
|
|
98
107
|
*
|
package/dist/internal/layout.js
CHANGED
|
@@ -104,6 +104,15 @@ export const PAGE_STACK = 'flex flex-col gap-section';
|
|
|
104
104
|
* crops the ring of every button inside it down to whichever edge fits.
|
|
105
105
|
*/
|
|
106
106
|
export const CARD_SURFACE = 'bg-surface border border-line rounded-xl';
|
|
107
|
+
/**
|
|
108
|
+
* The card surface in the danger tone, for a group of destructive actions.
|
|
109
|
+
*
|
|
110
|
+
* The fill is danger mixed into surface rather than a translucent tint, so it
|
|
111
|
+
* is opaque and paints the same on the page ground and inside a card. Five
|
|
112
|
+
* percent keeps the controls of a form inside it on a ground they were drawn
|
|
113
|
+
* for. The contrast suite reads the share from this string.
|
|
114
|
+
*/
|
|
115
|
+
export const PANEL_DANGER_SURFACE = 'bg-[color-mix(in_srgb,var(--color-danger)_5%,var(--color-surface))] border border-danger/40 rounded-xl';
|
|
107
116
|
/**
|
|
108
117
|
* Card padding by name.
|
|
109
118
|
*
|
package/dist/styles/theme.css
CHANGED
|
@@ -524,6 +524,12 @@ html[data-theme='light'] {
|
|
|
524
524
|
*
|
|
525
525
|
* Durations are not zeroed. An animation that finishes in 1ms still fires its
|
|
526
526
|
* animationend, so components that wait on one keep working.
|
|
527
|
+
*
|
|
528
|
+
* The View Transitions API animates pseudo-elements on the root, and `*`
|
|
529
|
+
* matches none of them, so a page transition still cross-faded. They take a
|
|
530
|
+
* rule of their own because a browser that does not know a pseudo-element
|
|
531
|
+
* drops the whole rule it appears in, and the element rule has to survive
|
|
532
|
+
* there.
|
|
527
533
|
*/
|
|
528
534
|
@media (prefers-reduced-motion: reduce) {
|
|
529
535
|
*,
|
|
@@ -534,6 +540,14 @@ html[data-theme='light'] {
|
|
|
534
540
|
transition-duration: 1ms !important;
|
|
535
541
|
scroll-behavior: auto !important;
|
|
536
542
|
}
|
|
543
|
+
|
|
544
|
+
::view-transition-group(*),
|
|
545
|
+
::view-transition-image-pair(*),
|
|
546
|
+
::view-transition-old(*),
|
|
547
|
+
::view-transition-new(*) {
|
|
548
|
+
animation-duration: 1ms !important;
|
|
549
|
+
animation-iteration-count: 1 !important;
|
|
550
|
+
}
|
|
537
551
|
}
|
|
538
552
|
}
|
|
539
553
|
|
package/package.json
CHANGED
package/src/lib/styles/theme.css
CHANGED
|
@@ -524,6 +524,12 @@ html[data-theme='light'] {
|
|
|
524
524
|
*
|
|
525
525
|
* Durations are not zeroed. An animation that finishes in 1ms still fires its
|
|
526
526
|
* animationend, so components that wait on one keep working.
|
|
527
|
+
*
|
|
528
|
+
* The View Transitions API animates pseudo-elements on the root, and `*`
|
|
529
|
+
* matches none of them, so a page transition still cross-faded. They take a
|
|
530
|
+
* rule of their own because a browser that does not know a pseudo-element
|
|
531
|
+
* drops the whole rule it appears in, and the element rule has to survive
|
|
532
|
+
* there.
|
|
527
533
|
*/
|
|
528
534
|
@media (prefers-reduced-motion: reduce) {
|
|
529
535
|
*,
|
|
@@ -534,6 +540,14 @@ html[data-theme='light'] {
|
|
|
534
540
|
transition-duration: 1ms !important;
|
|
535
541
|
scroll-behavior: auto !important;
|
|
536
542
|
}
|
|
543
|
+
|
|
544
|
+
::view-transition-group(*),
|
|
545
|
+
::view-transition-image-pair(*),
|
|
546
|
+
::view-transition-old(*),
|
|
547
|
+
::view-transition-new(*) {
|
|
548
|
+
animation-duration: 1ms !important;
|
|
549
|
+
animation-iteration-count: 1 !important;
|
|
550
|
+
}
|
|
537
551
|
}
|
|
538
552
|
}
|
|
539
553
|
|