@x-plat/design-system 0.5.32 → 0.5.33

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/index.cjs CHANGED
@@ -6750,40 +6750,28 @@ var useChartAnimation = (containerRef, dataKey) => {
6750
6750
  }, [dataKey]);
6751
6751
  return animate || prefersReducedMotion();
6752
6752
  };
6753
+ var TOOLTIP_OFFSET = 12;
6753
6754
  var useChartTooltip = (enabled) => {
6754
6755
  const [tooltip, setTooltip] = import_react6.default.useState({
6755
6756
  visible: false,
6756
- x: 0,
6757
- y: 0,
6757
+ clientX: 0,
6758
+ clientY: 0,
6758
6759
  content: ""
6759
6760
  });
6760
6761
  const containerRef = import_react6.default.useRef(null);
6761
6762
  const rafRef = import_react6.default.useRef(0);
6762
6763
  const move = import_react6.default.useCallback((e) => {
6763
6764
  if (!enabled) return;
6764
- const clientX = e.clientX;
6765
- const clientY = e.clientY;
6765
+ const cx = e.clientX;
6766
+ const cy = e.clientY;
6766
6767
  cancelAnimationFrame(rafRef.current);
6767
6768
  rafRef.current = requestAnimationFrame(() => {
6768
- const rect = containerRef.current?.getBoundingClientRect();
6769
- if (!rect) return;
6770
- setTooltip((prev) => ({
6771
- ...prev,
6772
- x: clientX - rect.left,
6773
- y: clientY - rect.top - 12
6774
- }));
6769
+ setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
6775
6770
  });
6776
6771
  }, [enabled]);
6777
6772
  const show = import_react6.default.useCallback((e, content) => {
6778
6773
  if (!enabled) return;
6779
- const rect = containerRef.current?.getBoundingClientRect();
6780
- if (!rect) return;
6781
- setTooltip({
6782
- visible: true,
6783
- x: e.clientX - rect.left,
6784
- y: e.clientY - rect.top - 12,
6785
- content
6786
- });
6774
+ setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
6787
6775
  }, [enabled]);
6788
6776
  const hide = import_react6.default.useCallback(() => {
6789
6777
  cancelAnimationFrame(rafRef.current);
@@ -6817,14 +6805,14 @@ var AxisLabels = import_react6.default.memo(({ labels, count, chartW, height })
6817
6805
  AxisLabels.displayName = "AxisLabels";
6818
6806
  var useCrosshair = (seriesPoints, entries, labels, chartH) => {
6819
6807
  const [activeIndex, setActiveIndex] = import_react6.default.useState(null);
6820
- const [mouseX, setMouseX] = import_react6.default.useState(null);
6821
6808
  const handleMouseMove = import_react6.default.useCallback((e) => {
6822
6809
  const svg = e.currentTarget;
6823
6810
  const rect = svg.getBoundingClientRect();
6824
6811
  const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
6825
- setMouseX(mx);
6826
6812
  if (seriesPoints.length === 0 || seriesPoints[0].length === 0) return;
6827
6813
  const points = seriesPoints[0];
6814
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
6815
+ const threshold = step / 2;
6828
6816
  let closest = 0;
6829
6817
  let minDist = Math.abs(points[0].x - mx);
6830
6818
  for (let i = 1; i < points.length; i++) {
@@ -6834,11 +6822,10 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
6834
6822
  closest = i;
6835
6823
  }
6836
6824
  }
6837
- setActiveIndex(closest);
6825
+ setActiveIndex(minDist <= threshold ? closest : null);
6838
6826
  }, [seriesPoints]);
6839
6827
  const handleMouseLeave = import_react6.default.useCallback(() => {
6840
6828
  setActiveIndex(null);
6841
- setMouseX(null);
6842
6829
  }, []);
6843
6830
  const tooltipContent = import_react6.default.useMemo(() => {
6844
6831
  if (activeIndex === null) return "";
@@ -6847,7 +6834,13 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
6847
6834
  return p ? `${key}: ${p.v}` : "";
6848
6835
  }).filter(Boolean).join(" / ");
6849
6836
  }, [activeIndex, entries, seriesPoints]);
6850
- return { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent };
6837
+ const getTooltipAt = import_react6.default.useCallback((idx) => {
6838
+ return entries.map(([key], di) => {
6839
+ const p = seriesPoints[di]?.[idx];
6840
+ return p ? `${key}: ${p.v}` : "";
6841
+ }).filter(Boolean).join(" / ");
6842
+ }, [entries, seriesPoints]);
6843
+ return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
6851
6844
  };
6852
6845
  var LineChart = import_react6.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
6853
6846
  const entries = import_react6.default.useMemo(() => Object.entries(data), [data]);
@@ -6870,7 +6863,7 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6870
6863
  );
6871
6864
  const lineRefs = import_react6.default.useRef([]);
6872
6865
  const clipRef = import_react6.default.useRef(null);
6873
- const { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent } = useCrosshair(seriesPoints, entries, labels, chartH);
6866
+ const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
6874
6867
  import_react6.default.useEffect(() => {
6875
6868
  if (!animate) return;
6876
6869
  lineRefs.current.forEach((el) => {
@@ -6893,8 +6886,7 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6893
6886
  });
6894
6887
  }
6895
6888
  }, [animate, seriesPoints, width]);
6896
- const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
6897
- const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
6889
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
6898
6890
  const lineClipId = "line-area-clip";
6899
6891
  return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
6900
6892
  "svg",
@@ -6903,7 +6895,26 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6903
6895
  className: "chart-svg",
6904
6896
  onMouseMove: (e) => {
6905
6897
  handleMouseMove(e);
6906
- onMove(e);
6898
+ const svg = e.currentTarget;
6899
+ const rect = svg.getBoundingClientRect();
6900
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
6901
+ const points = seriesPoints[0];
6902
+ if (!points || points.length === 0) return;
6903
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
6904
+ let closest = 0;
6905
+ let minDist = Math.abs(points[0].x - mx);
6906
+ for (let i = 1; i < points.length; i++) {
6907
+ const dist = Math.abs(points[i].x - mx);
6908
+ if (dist < minDist) {
6909
+ minDist = dist;
6910
+ closest = i;
6911
+ }
6912
+ }
6913
+ if (minDist <= step / 2) {
6914
+ onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
6915
+ } else {
6916
+ onLeave();
6917
+ }
6907
6918
  },
6908
6919
  onMouseLeave: () => {
6909
6920
  handleMouseLeave();
@@ -6958,21 +6969,16 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6958
6969
  )
6959
6970
  ] }, di);
6960
6971
  }),
6961
- guideX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6972
+ activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6962
6973
  "line",
6963
6974
  {
6964
- x1: guideX,
6975
+ x1: activeX,
6965
6976
  y1: PADDING.top,
6966
- x2: guideX,
6977
+ x2: activeX,
6967
6978
  y2: PADDING.top + chartH,
6968
6979
  className: "chart-crosshair"
6969
6980
  }
6970
6981
  ),
6971
- activeIndex !== null && activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("foreignObject", { x: activeX - 100, y: 0, width: "200", height: PADDING.top, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-crosshair-label", children: [
6972
- labels[activeIndex],
6973
- " \u2014 ",
6974
- tooltipContent
6975
- ] }) }),
6976
6982
  /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6977
6983
  "rect",
6978
6984
  {
@@ -7010,7 +7016,7 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
7010
7016
  );
7011
7017
  const lineRefs = import_react6.default.useRef([]);
7012
7018
  const curveClipRef = import_react6.default.useRef(null);
7013
- const { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent } = useCrosshair(seriesPoints, entries, labels, chartH);
7019
+ const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
7014
7020
  import_react6.default.useEffect(() => {
7015
7021
  if (!animate) return;
7016
7022
  lineRefs.current.forEach((el) => {
@@ -7033,8 +7039,7 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
7033
7039
  });
7034
7040
  }
7035
7041
  }, [animate, seriesPoints, width]);
7036
- const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
7037
- const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
7042
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
7038
7043
  const curveClipId = "curve-area-clip";
7039
7044
  return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
7040
7045
  "svg",
@@ -7043,7 +7048,26 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
7043
7048
  className: "chart-svg",
7044
7049
  onMouseMove: (e) => {
7045
7050
  handleMouseMove(e);
7046
- onMove(e);
7051
+ const svg = e.currentTarget;
7052
+ const rect = svg.getBoundingClientRect();
7053
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
7054
+ const points = seriesPoints[0];
7055
+ if (!points || points.length === 0) return;
7056
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
7057
+ let closest = 0;
7058
+ let minDist = Math.abs(points[0].x - mx);
7059
+ for (let i = 1; i < points.length; i++) {
7060
+ const dist = Math.abs(points[i].x - mx);
7061
+ if (dist < minDist) {
7062
+ minDist = dist;
7063
+ closest = i;
7064
+ }
7065
+ }
7066
+ if (minDist <= step / 2) {
7067
+ onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
7068
+ } else {
7069
+ onLeave();
7070
+ }
7047
7071
  },
7048
7072
  onMouseLeave: () => {
7049
7073
  handleMouseLeave();
@@ -7098,21 +7122,16 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
7098
7122
  )
7099
7123
  ] }, di);
7100
7124
  }),
7101
- guideX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7125
+ activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7102
7126
  "line",
7103
7127
  {
7104
- x1: guideX,
7128
+ x1: activeX,
7105
7129
  y1: PADDING.top,
7106
- x2: guideX,
7130
+ x2: activeX,
7107
7131
  y2: PADDING.top + chartH,
7108
7132
  className: "chart-crosshair"
7109
7133
  }
7110
7134
  ),
7111
- activeIndex !== null && activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("foreignObject", { x: activeX - 100, y: 0, width: "200", height: PADDING.top, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-crosshair-label", children: [
7112
- labels[activeIndex],
7113
- " \u2014 ",
7114
- tooltipContent
7115
- ] }) }),
7116
7135
  /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7117
7136
  "rect",
7118
7137
  {
@@ -7291,30 +7310,70 @@ var PieDonutChart = import_react6.default.memo(
7291
7310
  }
7292
7311
  );
7293
7312
  PieDonutChart.displayName = "PieDonutChart";
7294
- var TooltipBubble = ({ x, y, containerWidth, children }) => {
7313
+ var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
7295
7314
  const ref = import_react6.default.useRef(null);
7296
- const [adjustedX, setAdjustedX] = import_react6.default.useState(x);
7297
- import_react6.default.useEffect(() => {
7315
+ const [pos, setPos] = import_react6.default.useState({ left: 0, top: 0 });
7316
+ import_react6.default.useLayoutEffect(() => {
7298
7317
  const el = ref.current;
7299
7318
  if (!el) return;
7300
7319
  const w = el.offsetWidth;
7301
- const half = w / 2;
7302
- const margin = 8;
7303
- let nx = x;
7304
- if (x - half < margin) nx = half + margin;
7305
- else if (x + half > containerWidth - margin) nx = containerWidth - half - margin;
7306
- setAdjustedX(nx);
7307
- }, [x, containerWidth]);
7320
+ const h = el.offsetHeight;
7321
+ const vw = window.innerWidth;
7322
+ let left = clientX + TOOLTIP_OFFSET;
7323
+ let top = clientY - h - TOOLTIP_OFFSET;
7324
+ if (left + w > vw - 8) left = clientX - w - TOOLTIP_OFFSET;
7325
+ if (top < 8) top = clientY + TOOLTIP_OFFSET;
7326
+ if (left < 8) left = 8;
7327
+ setPos({ left, top });
7328
+ }, [clientX, clientY]);
7308
7329
  return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7309
7330
  "div",
7310
7331
  {
7311
7332
  ref,
7312
- className: "chart-tooltip",
7313
- style: { left: adjustedX, top: y },
7333
+ className: `chart-tooltip ${visible ? "chart-tooltip-show" : "chart-tooltip-hide"}`,
7334
+ style: { position: "fixed", left: pos.left, top: pos.top, zIndex: 1100 },
7314
7335
  children
7315
7336
  }
7316
7337
  );
7317
7338
  };
7339
+ var ChartLegend = ({ data, labels, type }) => {
7340
+ const entries = Object.entries(data);
7341
+ if (type === "pie" || type === "doughnut") {
7342
+ const values = entries.flatMap(([, v]) => v);
7343
+ const total = values.reduce((a, b) => a + b, 0) || 1;
7344
+ const firstKey = entries[0]?.[0] ?? "";
7345
+ const colorOffset = hashString(firstKey);
7346
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("div", { className: "chart-legend", children: values.map((v, i) => {
7347
+ const pct = Math.round(v / total * 100);
7348
+ const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
7349
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-item", children: [
7350
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
7351
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-text", children: [
7352
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
7353
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("span", { className: "chart-legend-value", children: [
7354
+ v.toLocaleString(),
7355
+ "(",
7356
+ pct,
7357
+ "%)"
7358
+ ] })
7359
+ ] })
7360
+ ] }, i);
7361
+ }) });
7362
+ }
7363
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("div", { className: "chart-legend", children: entries.map(([key], di) => {
7364
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
7365
+ const color = palette[2];
7366
+ const values = entries[di][1];
7367
+ const sum = values.reduce((a, b) => a + b, 0);
7368
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-item", children: [
7369
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
7370
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-text", children: [
7371
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-label", children: key }),
7372
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-value", children: sum.toLocaleString() })
7373
+ ] })
7374
+ ] }, di);
7375
+ }) });
7376
+ };
7318
7377
  var Chart = import_react6.default.memo((props) => {
7319
7378
  const { type, data, labels, tooltip: showTooltip = true } = props;
7320
7379
  const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
@@ -7330,7 +7389,8 @@ var Chart = import_react6.default.memo((props) => {
7330
7389
  ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
7331
7390
  ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
7332
7391
  ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
7333
- tooltip.visible && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(TooltipBubble, { x: tooltip.x, y: tooltip.y, containerWidth: width, children: tooltip.content })
7392
+ ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(ChartLegend, { data: stableData, labels: stableLabels, type }),
7393
+ tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content })
7334
7394
  ] });
7335
7395
  });
7336
7396
  Chart.displayName = "Chart";
package/dist/index.css CHANGED
@@ -2000,9 +2000,9 @@
2000
2000
  opacity: 1;
2001
2001
  }
2002
2002
  .lib-xplat-chart .chart-crosshair {
2003
- stroke: var(--semantic-border-subtle);
2003
+ stroke: var(--semantic-border-strong);
2004
2004
  stroke-width: 1;
2005
- stroke-dasharray: 4 2;
2005
+ stroke-dasharray: 4 3;
2006
2006
  pointer-events: none;
2007
2007
  }
2008
2008
  .lib-xplat-chart .chart-point-active {
@@ -2018,18 +2018,24 @@
2018
2018
  overflow: visible;
2019
2019
  }
2020
2020
  .lib-xplat-chart .chart-tooltip {
2021
- position: absolute;
2022
- transform: translate(-50%, -100%);
2023
- padding: var(--spacing-space-2) var(--spacing-space-3);
2021
+ padding: var(--spacing-space-3);
2024
2022
  background-color: var(--semantic-surface-neutral-strong);
2025
2023
  color: var(--semantic-text-inverse);
2026
2024
  font-size: 12px;
2025
+ line-height: 18px;
2027
2026
  font-weight: 500;
2028
2027
  border-radius: var(--spacing-radius-md);
2029
- white-space: nowrap;
2028
+ max-width: 240px;
2030
2029
  pointer-events: none;
2031
- z-index: 10;
2032
- animation: chart-tooltip-in 150ms ease-out;
2030
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
2031
+ }
2032
+ .lib-xplat-chart .chart-tooltip.chart-tooltip-show {
2033
+ opacity: 1;
2034
+ animation: chart-tooltip-in 120ms ease-out;
2035
+ }
2036
+ .lib-xplat-chart .chart-tooltip.chart-tooltip-hide {
2037
+ opacity: 0;
2038
+ animation: chart-tooltip-out 80ms ease-in;
2033
2039
  }
2034
2040
  .lib-xplat-chart .chart-bar-animate {
2035
2041
  animation: chart-bar-grow 800ms ease-out both;
@@ -2041,6 +2047,38 @@
2041
2047
  .lib-xplat-chart .chart-area[style*=animationDelay] {
2042
2048
  animation: chart-fade-in 800ms ease-out both;
2043
2049
  }
2050
+ .lib-xplat-chart .chart-legend {
2051
+ display: flex;
2052
+ flex-wrap: wrap;
2053
+ justify-content: center;
2054
+ gap: var(--spacing-space-4);
2055
+ padding: var(--spacing-space-3) 0;
2056
+ }
2057
+ .lib-xplat-chart .chart-legend-item {
2058
+ display: flex;
2059
+ align-items: flex-start;
2060
+ gap: var(--spacing-space-2);
2061
+ }
2062
+ .lib-xplat-chart .chart-legend-dot {
2063
+ flex-shrink: 0;
2064
+ width: 10px;
2065
+ height: 10px;
2066
+ border-radius: 50%;
2067
+ margin-top: 3px;
2068
+ }
2069
+ .lib-xplat-chart .chart-legend-text {
2070
+ display: flex;
2071
+ flex-direction: column;
2072
+ }
2073
+ .lib-xplat-chart .chart-legend-label {
2074
+ font-size: 12px;
2075
+ color: var(--semantic-text-muted);
2076
+ }
2077
+ .lib-xplat-chart .chart-legend-value {
2078
+ font-size: 14px;
2079
+ font-weight: 600;
2080
+ color: var(--semantic-text-strong);
2081
+ }
2044
2082
  @keyframes chart-bar-grow {
2045
2083
  from {
2046
2084
  transform: scaleY(0);
@@ -2060,11 +2098,17 @@
2060
2098
  @keyframes chart-tooltip-in {
2061
2099
  from {
2062
2100
  opacity: 0;
2063
- transform: translate(-50%, -90%);
2064
2101
  }
2065
2102
  to {
2066
2103
  opacity: 1;
2067
- transform: translate(-50%, -100%);
2104
+ }
2105
+ }
2106
+ @keyframes chart-tooltip-out {
2107
+ from {
2108
+ opacity: 1;
2109
+ }
2110
+ to {
2111
+ opacity: 0;
2068
2112
  }
2069
2113
  }
2070
2114
  @media (prefers-reduced-motion: reduce) {