@talelk/newui-tailwind 1.0.0 → 1.0.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talelk/newui-tailwind",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Tailwind CSS v4 layer: the newui design tokens as @theme, @utility and plugins.",
6
6
  "exports": {
@@ -16,8 +16,13 @@
16
16
  "files": [
17
17
  "src"
18
18
  ],
19
+ "scripts": {
20
+ "build": "tsx scripts/build-css.ts",
21
+ "build:css": "tsx scripts/build-css.ts",
22
+ "typecheck": "tsc --noEmit -p tsconfig.json"
23
+ },
19
24
  "dependencies": {
20
- "@talelk/newui-tokens": "0.1.0"
25
+ "@talelk/newui-tokens": "workspace:*"
21
26
  },
22
27
  "devDependencies": {
23
28
  "@tailwindcss/forms": "^0.5.11",
@@ -37,10 +42,5 @@
37
42
  "type": "git",
38
43
  "url": "git+https://github.com/talelk/newui.git",
39
44
  "directory": "packages/tailwind"
40
- },
41
- "scripts": {
42
- "build": "tsx scripts/build-css.ts",
43
- "build:css": "tsx scripts/build-css.ts",
44
- "typecheck": "tsc --noEmit -p tsconfig.json"
45
45
  }
46
- }
46
+ }
@@ -59,6 +59,29 @@ const PAIRS: Pair[] = [
59
59
  what: 'chart subtitle and card caption',
60
60
  },
61
61
 
62
+ // the shared overlay panel (atoms/lib overlayPanel + listboxRow): body ink
63
+ // and the checked step on the panel and on the highlighted row
64
+ {
65
+ fg: 'ink-gray-8',
66
+ bg: 'surface-elevation-1',
67
+ what: 'overlay panel body and listbox row',
68
+ },
69
+ {
70
+ fg: 'ink-gray-8',
71
+ bg: 'surface-gray-3',
72
+ what: 'highlighted listbox row',
73
+ },
74
+ {
75
+ fg: 'ink-blue-8',
76
+ bg: 'surface-elevation-1',
77
+ what: 'checked listbox row',
78
+ },
79
+ {
80
+ fg: 'ink-blue-8',
81
+ bg: 'surface-gray-3',
82
+ what: 'checked and highlighted listbox row',
83
+ },
84
+
62
85
  // a board column is the one tinted region muted text lands on
63
86
  { fg: 'ink-gray-8', bg: 'surface-gray-1', what: 'column heading' },
64
87
  { fg: 'ink-gray-6', bg: 'surface-gray-1', what: 'muted on a board column' },
@@ -67,6 +90,14 @@ const PAIRS: Pair[] = [
67
90
  { fg: 'ink-blue-link', bg: 'surface-elevation-1', what: 'blue link on card' },
68
91
  { fg: 'ink-blue-link', bg: 'surface-gray-1', what: 'blue link on canvas' },
69
92
 
93
+ // required marker on a label, on the page or a card
94
+ { fg: 'ink-red-8', bg: 'surface-base', what: 'required marker on base' },
95
+ {
96
+ fg: 'ink-red-8',
97
+ bg: 'surface-elevation-1',
98
+ what: 'required marker on a card',
99
+ },
100
+
70
101
  {
71
102
  fg: 'ink-blue-8',
72
103
  bg: 'surface-blue-1',
@@ -158,3 +189,29 @@ for (const mode of ['light', 'dark'] as Mode[]) {
158
189
  ).toBeGreaterThanOrEqual(3)
159
190
  })
160
191
  }
192
+
193
+ /**
194
+ * Solid buttons use the `-9` surface on hover and while open/pressed. The ramp
195
+ * inverts with the theme: `-9` is darker than the resting `-8` in light mode
196
+ * and lighter in dark mode. The foreground stays `ink-base`, so both
197
+ * directions are asserted — this is the pair that ships while the pointer is
198
+ * on the button, separate from the resting pair above.
199
+ */
200
+ describe('solid button state contract (WCAG AA)', () => {
201
+ const SOLID_THEMES = ['gray', 'blue', 'green', 'red'] as const
202
+
203
+ for (const theme of SOLID_THEMES) {
204
+ for (const mode of ['light', 'dark'] as Mode[]) {
205
+ it(`${mode}: ${theme} solid hover/open ≥ ${AA}`, () => {
206
+ const ratio = contrast(
207
+ resolve(mode, 'ink-base'),
208
+ resolve(mode, `surface-${theme}-9`),
209
+ )
210
+ expect(
211
+ Number(ratio.toFixed(2)),
212
+ `ink-base on surface-${theme}-9 = ${ratio.toFixed(2)}:1`,
213
+ ).toBeGreaterThanOrEqual(AA)
214
+ })
215
+ }
216
+ }
217
+ })
@@ -3,33 +3,61 @@
3
3
  @supports (font-variation-settings: normal) {
4
4
  :root { font-family: "InterVar", sans-serif; font-optical-sizing: auto; }
5
5
  } */
6
+
7
+ /* Latin is the common download. Extended scripts and CJK load only when used. */
6
8
  @font-face {
7
9
  font-family: "InterVar";
8
10
  font-weight: 100 900;
9
11
  font-display: swap;
10
12
  font-style: normal;
11
- src:
12
- url("Inter.var.woff2?v=3.19") format("woff2-variations"),
13
- url("Inter.var.woff2?v=3.19") format("woff2");
14
- src: url("Inter.var.woff2?v=3.19") format("woff2") tech("variations");
13
+ src: url("Inter-latin.var.woff2?v=3.19") format("woff2");
14
+ unicode-range: U+0000-02FF, U+2000-2FFF, U+FEFF, U+FFFD;
15
+ }
16
+ @font-face {
17
+ font-family: "InterVar";
18
+ font-weight: 100 900;
19
+ font-display: swap;
20
+ font-style: normal;
21
+ src: url("Inter-extended.var.woff2?v=3.19") format("woff2");
15
22
  unicode-range:
16
- U+0000-05FF, /* Latin, Greek, Cyrillic, Hebrew */ U+0700-1FFF,
17
- /* Extended Latin, Greek, Cyrillic, Armenian, Georgian */ U+2C00-2FFF,
18
- /* Additional scripts */ U+4E00-9FFF,
19
- /* CJK (Chinese, Japanese, Korean) */ U+AC00-D7AF; /* Hangul (Korean) */
23
+ U+0300-1FFF, U+2C00-2FFF, U+A000-AFFF, U+E000-F8FF, U+FDFC, U+FE20-FE2D,
24
+ U+1F12F-1F16B, U+1F850-1F852, U+FEFF, U+FFFD;
25
+ }
26
+ @font-face {
27
+ font-family: "InterVar";
28
+ font-weight: 100 900;
29
+ font-display: swap;
30
+ font-style: normal;
31
+ src: url("Inter-cjk.var.woff2?v=3.19") format("woff2");
32
+ unicode-range: U+4E00-9FFF, U+AC00-D7AF, U+FEFF, U+FFFD;
20
33
  }
21
34
  @font-face {
22
35
  font-family: "InterVar";
23
36
  font-weight: 100 900;
24
37
  font-display: swap;
25
38
  font-style: italic;
26
- src:
27
- url("Inter-Italic.var.woff2?v=3.19") format("woff2-variations"),
28
- url("Inter-Italic.var.woff2?v=3.19") format("woff2");
29
- src: url("Inter-Italic.var.woff2?v=3.19") format("woff2") tech("variations");
39
+ src: url("Inter-Italic-latin.var.woff2?v=3.19") format("woff2");
40
+ unicode-range: U+0000-02FF, U+2000-2FFF, U+FEFF, U+FFFD;
41
+ }
42
+ @font-face {
43
+ font-family: "InterVar";
44
+ font-weight: 100 900;
45
+ font-display: swap;
46
+ font-style: italic;
47
+ src: url("Inter-Italic-extended.var.woff2?v=3.19") format("woff2");
30
48
  unicode-range:
31
- U+0000-05FF, U+0700-1FFF, U+2C00-2FFF, U+4E00-9FFF, U+AC00-D7AF;
49
+ U+0300-1FFF, U+2C00-2FFF, U+A000-AFFF, U+E000-F8FF, U+FDFC, U+FE20-FE2D,
50
+ U+1F12F-1F16B, U+1F850-1F852, U+FEFF, U+FFFD;
51
+ }
52
+ @font-face {
53
+ font-family: "InterVar";
54
+ font-weight: 100 900;
55
+ font-display: swap;
56
+ font-style: italic;
57
+ src: url("Inter-Italic-cjk.var.woff2?v=3.19") format("woff2");
58
+ unicode-range: U+4E00-9FFF, U+AC00-D7AF, U+FEFF, U+FFFD;
32
59
  }
60
+
33
61
  /* Aliased 'InterVar' font as 'Inter' */
34
62
  /* To prevent breaking usage of `font-family: 'Inter'` */
35
63
  @font-face {
@@ -37,25 +65,50 @@
37
65
  font-weight: 100 900;
38
66
  font-display: swap;
39
67
  font-style: normal;
40
- src:
41
- url("Inter.var.woff2?v=3.19") format("woff2-variations"),
42
- url("Inter.var.woff2?v=3.19") format("woff2");
43
- src: url("Inter.var.woff2?v=3.19") format("woff2") tech("variations");
68
+ src: url("Inter-latin.var.woff2?v=3.19") format("woff2");
69
+ unicode-range: U+0000-02FF, U+2000-2FFF, U+FEFF, U+FFFD;
70
+ }
71
+ @font-face {
72
+ font-family: "Inter";
73
+ font-weight: 100 900;
74
+ font-display: swap;
75
+ font-style: normal;
76
+ src: url("Inter-extended.var.woff2?v=3.19") format("woff2");
44
77
  unicode-range:
45
- U+0000-05FF, /* Latin, Greek, Cyrillic, Hebrew */ U+0700-1FFF,
46
- /* Extended Latin, Greek, Cyrillic, Armenian, Georgian */ U+2C00-2FFF,
47
- /* Additional scripts */ U+4E00-9FFF,
48
- /* CJK (Chinese, Japanese, Korean) */ U+AC00-D7AF; /* Hangul (Korean) */
78
+ U+0300-1FFF, U+2C00-2FFF, U+A000-AFFF, U+E000-F8FF, U+FDFC, U+FE20-FE2D,
79
+ U+1F12F-1F16B, U+1F850-1F852, U+FEFF, U+FFFD;
80
+ }
81
+ @font-face {
82
+ font-family: "Inter";
83
+ font-weight: 100 900;
84
+ font-display: swap;
85
+ font-style: normal;
86
+ src: url("Inter-cjk.var.woff2?v=3.19") format("woff2");
87
+ unicode-range: U+4E00-9FFF, U+AC00-D7AF, U+FEFF, U+FFFD;
49
88
  }
50
89
  @font-face {
51
90
  font-family: "Inter";
52
91
  font-weight: 100 900;
53
92
  font-display: swap;
54
93
  font-style: italic;
55
- src:
56
- url("Inter-Italic.var.woff2?v=3.19") format("woff2-variations"),
57
- url("Inter-Italic.var.woff2?v=3.19") format("woff2");
58
- src: url("Inter-Italic.var.woff2?v=3.19") format("woff2") tech("variations");
94
+ src: url("Inter-Italic-latin.var.woff2?v=3.19") format("woff2");
95
+ unicode-range: U+0000-02FF, U+2000-2FFF, U+FEFF, U+FFFD;
96
+ }
97
+ @font-face {
98
+ font-family: "Inter";
99
+ font-weight: 100 900;
100
+ font-display: swap;
101
+ font-style: italic;
102
+ src: url("Inter-Italic-extended.var.woff2?v=3.19") format("woff2");
59
103
  unicode-range:
60
- U+0000-05FF, U+0700-1FFF, U+2C00-2FFF, U+4E00-9FFF, U+AC00-D7AF;
104
+ U+0300-1FFF, U+2C00-2FFF, U+A000-AFFF, U+E000-F8FF, U+FDFC, U+FE20-FE2D,
105
+ U+1F12F-1F16B, U+1F850-1F852, U+FEFF, U+FFFD;
106
+ }
107
+ @font-face {
108
+ font-family: "Inter";
109
+ font-weight: 100 900;
110
+ font-display: swap;
111
+ font-style: italic;
112
+ src: url("Inter-Italic-cjk.var.woff2?v=3.19") format("woff2");
113
+ unicode-range: U+4E00-9FFF, U+AC00-D7AF, U+FEFF, U+FFFD;
61
114
  }
@@ -126,6 +126,7 @@
126
126
  --color-surface-pink-8: var(--surface-pink-8);
127
127
  --color-surface-pink-9: var(--surface-pink-9);
128
128
  --color-surface-pink-10: var(--surface-pink-10);
129
+ --color-surface-scrim: var(--surface-scrim);
129
130
  --color-surface-alpha-base: var(--surface-alpha-base);
130
131
  --color-surface-alpha-gray-1: var(--surface-alpha-gray-1);
131
132
  --color-surface-alpha-gray-2: var(--surface-alpha-gray-2);
@@ -893,6 +894,13 @@
893
894
  --spacing-page-gutter: 1.5rem;
894
895
  --spacing-page-gutter-wide: 2rem;
895
896
  --spacing-page-stack: 2rem;
897
+ --spacing-menu-pad: 0.25rem;
898
+ --spacing-dialog-pad: 1rem;
899
+ --spacing-table-cell-x: 1rem;
900
+ --spacing-table-cell-y: 0.5rem;
901
+ --spacing-toolbar-pad: 0.5rem;
902
+ --spacing-stack: 1rem;
903
+ --spacing-inline: 0.5rem;
896
904
 
897
905
  /* elevation — shadow-<step> resolves the runtime elevation var */
898
906
  --shadow-sm: var(--elevation-sm);
@@ -903,6 +911,14 @@
903
911
  --shadow-xl: var(--elevation-xl);
904
912
  --shadow-2xl: var(--elevation-2xl);
905
913
 
914
+ /* shadow roles — shadow-<role> resolves its mapped elevation step */
915
+ --shadow-card: var(--elevation-sm);
916
+ --shadow-raised: var(--elevation-base);
917
+ --shadow-popover: var(--elevation-md);
918
+ --shadow-dialog: var(--elevation-lg);
919
+ --shadow-toast: var(--elevation-xl);
920
+ --shadow-drag: var(--elevation-2xl);
921
+
906
922
  /* typography — text-<size> and text-<size>-<weight>, text + paragraph */
907
923
  --text-interface: 0.875rem;
908
924
  --text-interface--line-height: 1.25rem;
@@ -1451,6 +1467,15 @@
1451
1467
 
1452
1468
  /* fonts */
1453
1469
  --font-sans: 'InterVar', ui-sans-serif, system-ui, sans-serif;
1470
+ --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
1471
+
1472
+ /* breakpoints — xs/sm/md/lg/xl/2xl variants resolve the token screens, never a literal */
1473
+ --breakpoint-xs: 360px;
1474
+ --breakpoint-sm: 640px;
1475
+ --breakpoint-md: 768px;
1476
+ --breakpoint-lg: 1024px;
1477
+ --breakpoint-xl: 1280px;
1478
+ --breakpoint-2xl: 1536px;
1454
1479
 
1455
1480
  /* line-heights — leading-tighter pins single-line chrome to 1.15 */
1456
1481
  --leading-tighter: 1.15;
@@ -1461,9 +1486,21 @@
1461
1486
  --z-overlay: 50;
1462
1487
  --z-top: 100;
1463
1488
 
1464
- /* motion — duration-fast/base/slow and the standard ease */
1489
+ /* motion — named durations and eases; components name the speed, never a number */
1490
+ --duration-instant: 75ms;
1465
1491
  --duration-fast: 100ms;
1466
1492
  --duration-base: 150ms;
1493
+ --duration-moderate: 200ms;
1467
1494
  --duration-slow: 300ms;
1495
+ --duration-progress: 660ms;
1468
1496
  --ease-standard: cubic-bezier(0.65, 0, 0.35, 1);
1497
+ --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
1498
+ --ease-in: cubic-bezier(0.5, 0, 0.75, 0);
1499
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
1500
+
1501
+ /* animations — @keyframes live in utilities.css; speeds compose the tokens above */
1502
+ --animate-overlay-in: overlay-in 100ms cubic-bezier(0.23, 1, 0.32, 1);
1503
+ --animate-drawer-in: drawer-in 200ms cubic-bezier(0.23, 1, 0.32, 1);
1504
+ --animate-shimmer: shimmer 1400ms linear infinite;
1505
+ --animate-progress: progress 1400ms cubic-bezier(0.65, 0, 0.35, 1) infinite;
1469
1506
  }
@@ -125,6 +125,7 @@
125
125
  --surface-pink-8: oklch(0.481 0.17 346.479);
126
126
  --surface-pink-9: oklch(0.408 0.155 348.063);
127
127
  --surface-pink-10: oklch(0.316 0.115 345.791);
128
+ --surface-scrim: oklch(0 0 0 / 0.271);
128
129
  --surface-alpha-base: oklch(1 0 0);
129
130
  --surface-alpha-gray-1: oklch(0 0 0 / 0.031);
130
131
  --surface-alpha-gray-2: oklch(0 0 0 / 0.043);
@@ -674,6 +675,7 @@
674
675
  --surface-pink-8: oklch(0.667 0.192 346.848);
675
676
  --surface-pink-9: oklch(0.726 0.166 344.979);
676
677
  --surface-pink-10: oklch(0.796 0.128 343.745);
678
+ --surface-scrim: oklch(0 0 0 / 0.722);
677
679
  --surface-alpha-base: oklch(0 0 0 / 0.039);
678
680
  --surface-alpha-gray-1: oklch(1 0 0 / 0.055);
679
681
  --surface-alpha-gray-2: oklch(1 0 0 / 0.078);
@@ -1078,6 +1080,12 @@
1078
1080
  --dark-gray-alpha-800: oklch(1 0 0 / 0.055);
1079
1081
  --dark-gray-alpha-900: oklch(1 0 0 / 0.035);
1080
1082
  --dark-gray-alpha-950: oklch(0 0 0 / 0.039);
1083
+ --elevation-sm: inset 0px 0.5px 0.5px 0.5px #ffffff08, 0px 0px 14px 0px #0000002e, 0px 1px 3px 0px #000000b2;
1084
+ --elevation-base: inset 0px 0.5px 0.5px 0.5px #ffffff08, 0px 0px 14px 0px #0000002e, 0px 2px 5px 0px #00000099;
1085
+ --elevation-md: inset 0px 0.5px 0.5px 0.5px #ffffff08, 0px 0px 16px 2px #00000033, 0px 6px 12px -2px #00000099;
1086
+ --elevation-lg: inset 0px 0.5px 1.5px 0.5px #ffffff0a, 0px 0px 16px 0px #0000001a, 0px 18px 20px -8px #00000085;
1087
+ --elevation-xl: inset 0px 0.5px 1.5px 0.5px #ffffff0a, 0px 0px 14px 2px #0000001f, 0px 26px 34px -6px #0000006b;
1088
+ --elevation-2xl: inset 0px 0.5px 1.5px 0.5px #ffffff0f, 0px 0px 14px 10px #0000001f, 0px 44px 52px -4px #0000006b;
1081
1089
  --focus-outline-default: 3px solid #464646cc;
1082
1090
  --focus-outline-red: 3px solid #751819cc;
1083
1091
  --focus-outline-green: 3px solid #1d563bcc;
@@ -6,7 +6,9 @@ import {
6
6
  easing,
7
7
  fontSize,
8
8
  radius,
9
+ screens,
9
10
  semanticColors,
11
+ shadowRoles,
10
12
  shadows,
11
13
  spacing,
12
14
  tracking,
@@ -77,6 +79,9 @@ function expectedShadows(): Set<string> {
77
79
  if (key === 'none') continue
78
80
  names.add(key === 'DEFAULT' ? '--shadow' : `--shadow-${key}`)
79
81
  }
82
+ for (const role of Object.keys(shadowRoles)) {
83
+ names.add(`--shadow-${role}`)
84
+ }
80
85
  return names
81
86
  }
82
87
 
@@ -138,4 +143,19 @@ describe('generated theme parity with @talelk/newui-tokens', () => {
138
143
  const missing = expected.filter((name) => !defined.has(name))
139
144
  expect(missing).toEqual([])
140
145
  })
146
+
147
+ it('emits the breakpoint, animation and mono scales', () => {
148
+ // The animation names are composed in build-css.ts from the duration and
149
+ // easing tokens; the list is mirrored here so a dropped branch fails.
150
+ const expected = [
151
+ ...Object.keys(screens).map((key) => `--breakpoint-${key}`),
152
+ '--animate-overlay-in',
153
+ '--animate-drawer-in',
154
+ '--animate-shimmer',
155
+ '--animate-progress',
156
+ '--font-mono',
157
+ ]
158
+ const missing = expected.filter((name) => !defined.has(name))
159
+ expect(missing).toEqual([])
160
+ })
141
161
  })
package/src/utilities.css CHANGED
@@ -30,6 +30,17 @@
30
30
  outline-offset: 0px;
31
31
  }
32
32
 
33
+ /* The padded-row outline: the row (Checkbox/Switch `padded`) draws the ring
34
+ * around itself while its hidden control holds keyboard focus. Same outline
35
+ * mechanism as `focus-ring` — never a `ring-*` box-shadow, which would
36
+ * collide with `shadow-*` and vanish in forced-colors mode. */
37
+ @utility focus-ring-within {
38
+ &:has(:focus-visible) {
39
+ outline: var(--focus-outline-default);
40
+ outline-offset: 0px;
41
+ }
42
+ }
43
+
33
44
  /* Stacking and motion scales. The numbers live in `@theme` (`--z-*`,
34
45
  * `--duration-*`, generated from `@talelk/newui-tokens`); these utilities are the
35
46
  * named vocabulary components use, so a layer change is a token edit and the
@@ -46,15 +57,24 @@
46
57
  @utility z-top {
47
58
  z-index: var(--z-top);
48
59
  }
60
+ @utility duration-instant {
61
+ transition-duration: var(--duration-instant);
62
+ }
49
63
  @utility duration-fast {
50
64
  transition-duration: var(--duration-fast);
51
65
  }
52
66
  @utility duration-base {
53
67
  transition-duration: var(--duration-base);
54
68
  }
69
+ @utility duration-moderate {
70
+ transition-duration: var(--duration-moderate);
71
+ }
55
72
  @utility duration-slow {
56
73
  transition-duration: var(--duration-slow);
57
74
  }
75
+ @utility duration-progress {
76
+ transition-duration: var(--duration-progress);
77
+ }
58
78
 
59
79
  /* A disclosure panel — the body of an `Accordion` item or a `Collapsible`.
60
80
  *
@@ -74,15 +94,15 @@
74
94
  grid-template-rows: 1fr;
75
95
  visibility: visible;
76
96
  transition:
77
- grid-template-rows 200ms cubic-bezier(0.23, 1, 0.32, 1),
97
+ grid-template-rows var(--duration-moderate) var(--ease-out),
78
98
  visibility 0s 0s;
79
99
 
80
100
  &[data-state="closed"] {
81
101
  grid-template-rows: 0fr;
82
102
  visibility: hidden;
83
103
  transition:
84
- grid-template-rows 200ms cubic-bezier(0.23, 1, 0.32, 1),
85
- visibility 0s 200ms;
104
+ grid-template-rows var(--duration-moderate) var(--ease-out),
105
+ visibility 0s var(--duration-moderate);
86
106
  }
87
107
 
88
108
  @media (prefers-reduced-motion: reduce) {
@@ -105,6 +125,43 @@
105
125
  }
106
126
  }
107
127
 
128
+ /* Overlay entrances (speeds are the `--animate-*` tokens in `@theme`; the
129
+ * global reduced-motion block below collapses every one of them). Popups and
130
+ * modals fade in over a 2px settle — no scale, no slide theatrics. */
131
+ @keyframes overlay-in {
132
+ from {
133
+ opacity: 0;
134
+ transform: translateY(2px);
135
+ }
136
+ to {
137
+ opacity: 1;
138
+ transform: translateY(0);
139
+ }
140
+ }
141
+
142
+ /* Bottom-anchored sheets rise the last few pixels into place. */
143
+ @keyframes drawer-in {
144
+ from {
145
+ opacity: 0;
146
+ transform: translateY(8px);
147
+ }
148
+ to {
149
+ opacity: 1;
150
+ transform: translateY(0);
151
+ }
152
+ }
153
+
154
+ /* Skeleton sheen: sweeps a wide background gradient across the block. The
155
+ * consumer supplies the gradient and `background-size: 200% 100%`. */
156
+ @keyframes shimmer {
157
+ from {
158
+ background-position: 200% 0;
159
+ }
160
+ to {
161
+ background-position: -200% 0;
162
+ }
163
+ }
164
+
108
165
  /* Respect the OS "reduce motion" setting everywhere, once. Animations collapse
109
166
  * to a single near-instant frame instead of being removed, so nothing that
110
167
  * depends on an `animationend` (Zag's presence, our exit transitions) stalls. */
Binary file